Pelacak Kebiasaan: Tandai Satu Hari
Termasuk bagian Proyek dalam perjalanan Flutter di Coddy. Pelajaran 30 dari 36.
Ketuk lingkaran untuk menandai hari, lalu ketuk lagi untuk menghapus tandanya. Data kebiasaan tersimpan dalam state layar, tetapi lingkaran digambar jauh di dalam HabitCard. Ketukan itu harus diteruskan ke tempat data berada.
Inilah cara mengangkat state dengan callback. Layar mengelola perubahan:
void _toggle(Habit habit, int day) {
setState(() {
habit.days[day] = !habit.days[day];
});
}Layar memberikan setiap kartu sebuah fungsi, onToggle: (day) => _toggle(habit, day). Tipe fungsi tersebut pada kartu adalah void Function(int day), yaitu fungsi yang menerima nomor hari. Setiap lingkaran mendapatkan VoidCallback sederhana, onTap: () => onToggle(i), lalu membungkus Container-nya dengan GestureDetector yang memanggilnya.
!habit.days[day] membalik nilai bool: true menjadi false dan sebaliknya, sehingga satu fungsi dapat menandai maupun menghapus tanda.
Tantangan
SulitTandai hari:
- tambahkan method
_toggleke state layar - berikan
HabitCardrequiredonTogglebertipevoid Function(int day), lalu teruskan(day) => _toggle(habit, day) - berikan
DayCirclerequiredVoidCallback onTap, bungkusContainer-nya denganGestureDetector, lalu teruskan() => onToggle(i)
Coba sendiri
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,
),
),
);
}
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Proyek
1Proyek: Daftar Tugas
Daftar Tugas: Daftar TugasDaftar Tugas: Tambahkan Tugas4Proyek: Pelacak Kebiasaan
Pelacak Kebiasaan: KebiasaanmuPelacak Kebiasaan: Pekan IniLatihan sendiri: Compiler Flutter online