Penjelasan Aplikasi To Do List

1️⃣.Screenshoot Program Aplikasi To Do List


 2️⃣ Potongan Kode Program (yang ngontrol tampilan)

Bagian ini fokus ke UI utama yang biasanya muncul di screenshot: card tiap tugas (ListTile), layar utama (build) dan sheet tambah/edit tugas (AddTaskSheet).


// --- Potongan: Task item (card + listtile) ---

Widget _buildTaskItem(Task t) {

  return Card(

    margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),

    elevation: 2,

    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),

    child: ListTile(

      onTap: () => _openAddTaskSheet(editTask: t),

      leading: CircleAvatar(

        backgroundColor: t.color,

        child: Icon(

          t.done ? Icons.check : Icons.schedule,

          color: Colors.white,

        ),

      ),

      title: Text(

        t.title,

        style: TextStyle(

          fontWeight: FontWeight.w600,

          decoration: t.done ? TextDecoration.lineThrough : null,

        ),

      ),

      subtitle: Text('${_formatTime(t.time)} • ${t.category}'),

      trailing: Wrap(

        spacing: 4,

        children: [

          IconButton(

            icon: Icon(

              t.done ? Icons.check_circle : Icons.circle_outlined,

              color: t.done ? Colors.green : Colors.grey,

            ),

            onPressed: () => _toggleDone(t),

          ),

          IconButton(

            icon: const Icon(Icons.delete_forever_rounded, color: Colors.redAccent),

            onPressed: () => _deleteTask(t),

          ),

        ],

      ),

    ),

  );

}


// --- Potongan: Scaffold utama (list / empty state) ---

@override

Widget build(BuildContext context) {

  final now = DateTime.now();

  final dateLabel = '${now.day}/${now.month}/${now.year}';

  return Scaffold(

    appBar: AppBar(

      title: const Text("Jadwal Harian"),

      actions: [

        Padding(

          padding: const EdgeInsets.symmetric(horizontal: 12),

          child: Center(

            child: Text(dateLabel, style: const TextStyle(fontSize: 14)),

          ),

        )

      ],

    ),

    body: _tasks.isEmpty

        ? _buildEmpty()

        : ListView.builder(

            itemCount: _tasks.length,

            itemBuilder: (context, i) => _buildTaskItem(_tasks[i]),

          ),

    floatingActionButton: FloatingActionButton.extended(

      onPressed: () => _openAddTaskSheet(),

      icon: const Icon(Icons.add),

      label: const Text("Tambah"),

    ),

  );

}


// --- Potongan: Bottom sheet tambah / edit tugas (form) ---

class AddTaskSheet extends StatefulWidget {

  final void Function(String, TimeOfDay, String, Color) onSubmit;

  final Task? existing;

  const AddTaskSheet({super.key, required this.onSubmit, this.existing});

  // ...

}


class _AddTaskSheetState extends State<AddTaskSheet> {

  final _formKey = GlobalKey<FormState>();

  late String _title;

  late TimeOfDay _time;

  late String _category;

  late Color _color;


  final _categories = ['Rutinitas', 'Pribadi', 'Kerja', 'Belajar', 'Lainnya'];

  final _colors = [

    Colors.purple.shade300,

    Colors.orange.shade300,

    Colors.teal.shade300,

    Colors.blueGrey.shade300,

    Colors.pink.shade300,

  ];


  @override

  void initState() {

    super.initState();

    if (widget.existing != null) {

      _title = widget.existing!.title;

      _time = widget.existing!.time;

      _category = widget.existing!.category;

      _color = widget.existing!.color;

    } else {

      _title = '';

      _time = TimeOfDay.now();

      _category = _categories.first;

      _color = _colors.first;

    }

  }


  Future<void> _pickTime() async {

    final picked = await showTimePicker(context: context, initialTime: _time);

    if (picked != null) setState(() => _time = picked);

  }


  @override

  Widget build(BuildContext context) {

    final isEdit = widget.existing != null;

    return Padding(

      padding: EdgeInsets.only(

        bottom: MediaQuery.of(context).viewInsets.bottom,

        top: 16,

        left: 16,

        right: 16,

      ),

      child: Form(

        key: _formKey,

        child: ListView(

          shrinkWrap: true,

          children: [

            Text(isEdit ? "Edit Tugas" : "Tambah Tugas",

                style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),

            const SizedBox(height: 16),

            TextFormField(

              initialValue: _title,

              decoration: const InputDecoration(

                labelText: 'Nama Tugas',

                border: OutlineInputBorder(),

              ),

              validator: (v) =>

                  v == null || v.trim().isEmpty ? 'Masukkan nama tugas' : null,

              onChanged: (v) => _title = v,

            ),

            const SizedBox(height: 12),

            Row(

              children: [

                Expanded(

                  child: InkWell(

                    onTap: _pickTime,

                    child: InputDecorator(

                      decoration: const InputDecoration(

                        labelText: 'Waktu',

                        border: OutlineInputBorder(),

                      ),

                      child: Row(

                        children: [

                          const Icon(Icons.access_time),

                          const SizedBox(width: 8),

                          Text(_time.format(context)),

                        ],

                      ),

                    ),

                  ),

                ),

                const SizedBox(width: 12),

                Expanded(

                  child: DropdownButtonFormField<String>(

                    value: _category,

                    items: _categories

                        .map((c) => DropdownMenuItem(value: c, child: Text(c)))

                        .toList(),

                    onChanged: (v) => setState(() => _category = v!),

                    decoration: const InputDecoration(

                      labelText: 'Kategori',

                      border: OutlineInputBorder(),

                    ),

                  ),

                ),

              ],

            ),

            const SizedBox(height: 12),

            Wrap(

              spacing: 8,

              children: _colors

                  .map((c) => GestureDetector(

                        onTap: () => setState(() => _color = c),

                        child: Container(

                          width: 36,

                          height: 36,

                          decoration: BoxDecoration(

                            color: c,

                            borderRadius: BorderRadius.circular(6),

                            border: Border.all(

                              color: _color == c ? Colors.black54 : Colors.transparent,

                              width: 2,

                            ),

                          ),

                        ),

                      ))

                  .toList(),

            ),

            const SizedBox(height: 16),

            ElevatedButton.icon(

              onPressed: () {

                if (_formKey.currentState!.validate()) {

                  widget.onSubmit(_title.trim(), _time, _category, _color);

                }

              },

              icon: Icon(isEdit ? Icons.save : Icons.add),

              label: Text(isEdit ? 'Simpan Perubahan' : 'Tambah Tugas'),

            ),

            const SizedBox(height: 10),

          ],

        ),

      ),

    );

  }

}



3️⃣ Penjelasan Cara Kerja


Oke ini penjelasannya biar bisa kamu salin ke posting blog:

Tampilan daftar tugas: tiap tugas dirender sebagai Card yang berisi ListTile. Di kiri ada CircleAvatar warna tugas, di tengah judul + subtitle (waktu & kategori), dan di kanan ada tombol untuk toggle done & hapus. Ketuk ListTile = buka sheet edit.


Tambah / Edit tugas: pake showModalBottomSheet yang menampilkan AddTaskSheet. Form di sheet ini punya:


TextFormField untuk nama tugas (wajib diisi),


InkWell + showTimePicker untuk pilih jam,


DropdownButtonFormField untuk kategori,


pilihan warna (warna- warna kecil) yang bisa tap untuk pilih warna,


tombol submit (Tambah / Simpan) yang validasi form lalu panggil callback onSubmit.


Logika data:


Saat awal aplikasi initState() manggil _addSampleTasks() untuk mengisi contoh tugas (biar gak kosong).


Semua tugas disimpan di _tasks (List<Task>).


Setelah nambah atau edit, panggil _sortTasks() yang mengurutkan tugas berdasarkan jam supaya daftar terurut dari pagi ke malam.


Tombol check toggle memanggil _toggleDone() yang menandai tugas selesai/ belum, dan perubahan ini langsung dipanggil setState() agar UI update.


Hapus tugas pake _deleteTask() yang pertama tampilkan AlertDialog konfirmasi; kalau user pilih “Hapus”, tugas di-remove dan snack bar muncul.


UX kecil yang enak:


Waktu ditampilkeun pake _formatTime(...) biar selalu HH:MM.


Saat sheet muncul, padding bawah menyesuaikan MediaQuery.of(context).viewInsets.bottom supaya form gak ketutup keyboard.


Warna tiap tugas membantu visual untuk membedakan kategori/jenis tugas.

Komentar

Postingan populer dari blog ini

MEMBUAT TAMPILAN APLIKASI SEDERANA MENGGUNAKAN DART FLUTTER

Aplication Flutter With Image 7/31/2025