מעקב הרגלים: סימון יום
חלק מהיחידה פרויקטים במסלול ה-Flutter של Coddy. שיעור 30 מתוך 36.
יש להקיש על עיגול כדי לסמן את היום, ולהקיש עליו שוב כדי לבטל את הסימון. ההרגלים נמצאים במצב של המסך, אבל העיגול מצויר עמוק בתוך HabitCard. ההקשה צריכה לעבור למעלה, אל המקום שבו נמצאים הנתונים.
כך מעלים את המצב בעזרת פונקציות callback. המסך אחראי לשינוי:
void _toggle(Habit habit, int day) {
setState(() {
habit.days[day] = !habit.days[day];
});
}הוא מעביר לכל כרטיס פונקציה, onToggle: (day) => _toggle(habit, day). הטיפוס של הפונקציה בכרטיס הוא void Function(int day), פונקציה שמקבלת את מספר היום. כל עיגול מקבל VoidCallback פשוט, onTap: () => onToggle(i), ועוטף את ה-Container שלו ב-GestureDetector שקורא לה.
!habit.days[day] הופך ערך bool: true הופך ל-false ולהפך, כך שפונקציה אחת גם מסמנת וגם מבטלת את הסימון.
אתגר
קשהסמנו ימים:
- הוסיפו את המתודה
_toggleלמצב של המסך - הוסיפו ל־
HabitCardאתonToggleמסוגvoid Function(int day)בתורrequired, והעבירו את(day) => _toggle(habit, day) - הוסיפו ל־
DayCircleאתVoidCallback onTapבתורrequired, עטפו את ה־Containerשלו ב־GestureDetector, והעבירו את() => onToggle(i)
נסו בעצמכם
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
),
home: const HabitScreen(),
);
}
}
const List<String> dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
class Habit {
Habit(this.name, this.days);
final String name;
final List<bool> days;
}
class HabitScreen extends StatefulWidget {
const HabitScreen({super.key});
@override
State<HabitScreen> createState() => _HabitScreenState();
}
class _HabitScreenState extends State<HabitScreen> {
final List<Habit> _habits = [
Habit('Drink water', [true, true, true, false, true, false, false]),
Habit('Read 10 pages', [true, false, true, true, false, false, false]),
Habit('Stretch', List.filled(7, false)),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Habits')),
body: ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 88),
children: [
for (Habit habit in _habits) HabitCard(habit: habit),
],
),
);
}
}
class HabitCard extends StatelessWidget {
const HabitCard({super.key, required this.habit});
final Habit habit;
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Card(
margin: const EdgeInsets.only(top: 12),
clipBehavior: Clip.antiAlias,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(habit.name, style: textTheme.titleMedium),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
for (int i = 0; i < 7; i++) DayCircle(label: dayNames[i], done: habit.days[i]),
],
),
],
),
),
);
}
}
class DayCircle extends StatelessWidget {
const DayCircle({super.key, required this.label, required this.done});
final String label;
final bool done;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Container(
width: 40,
height: 40,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: done ? colors.primary : colors.surfaceContainerHighest,
),
child: Text(
label,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: done ? colors.onPrimary : colors.onSurfaceVariant,
),
),
);
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה פרויקטים
תרגלו בעצמכם: קומפיילר Flutter אונליין