Skip to content

🛠️ Mini Proyek: State Management

⭐⭐⭐ · 6–10 jam

Apa yang akan kamu bangun

Pelacak Progres Baca — aplikasi untuk mengelola beberapa buku yang sedang kamu baca sekaligus. Setiap buku punya jumlah halaman total, halaman yang sudah kamu capai, dan tanggal target selesai. Setiap kali kamu memperbarui posisi baca, aplikasi mencatatnya sebagai satu sesi, lalu menurunkan statistik dari catatan itu: rata-rata halaman per hari, perkiraan tanggal selesai, dan berapa buku yang tertinggal dari targetnya.

Ini bukan lanjutan Mini Proyek Bagian I

Mini proyek Bagian I juga bertema buku, tetapi keduanya berdiri sendiri. Mulai dari proyek kosong. Kalau kamu belum mengerjakan yang Bagian I, kamu tidak kehilangan apa pun di sini.

Kenapa aplikasi ini, dan bukan pencatat tugas biasa? Karena statistiknya adalah data turunan, dan data turunan adalah tempat state management membuktikan nilainya. Rata-rata halaman per hari tidak boleh disimpan sebagai variabel yang kamu perbarui manual setiap kali ada sesi baru — ia harus dihitung dari state utama, otomatis, setiap kali state itu berubah. Di Riverpod, itulah provider yang mengawasi provider lain (Bab 18). Di BLoC, itulah Cubit yang berlangganan stream Cubit lain, atau lebih baik lagi, dua BLoC yang sama-sama mendengarkan satu repository (Bab 24).

Ada satu bagian yang sengaja dibuat merepotkan: penyaring. Pengguna bisa menyaring daftar menjadi "sedang dibaca", "selesai", atau "tertinggal dari target". Penyaring itu adalah state juga, terpisah dari daftar buku, dan daftar yang tampil di layar adalah hasil kombinasi keduanya. Kalau kamu menyimpan daftar tersaring sebagai state ketiga, kamu akan menemukan sendiri kenapa itu ide buruk.

Karena Bagian III adalah tempat pemisahan lapisan pertama kali diajarkan secara serius (Bab 22), proyek ini juga menuntut struktur berlapis — dan menuntut pengujian, karena lapisan logika yang bersih memang dibuat supaya bisa diuji tanpa membangun satu widget pun (Bab 26).

Prasyarat

Selesaikan dulu:

Requirement fungsional

  • Daftar buku yang sedang dibaca, masing-masing menampilkan judul, posisi halaman, dan persentase progres.
  • Menambah buku baru: judul, penulis, total halaman, tanggal target selesai.
  • Memperbarui posisi halaman sebuah buku. Setiap pembaruan tercatat sebagai sesi baca dengan waktunya sendiri.
  • Buku otomatis berpindah ke keadaan "selesai" ketika posisi halaman mencapai total halaman.
  • Layar statistik menampilkan angka yang seluruhnya diturunkan dari sesi: rata-rata halaman per hari, perkiraan tanggal selesai per buku, dan total halaman yang dibaca minggu ini.
  • Penyaring daftar: semua, sedang dibaca, selesai, dan tertinggal dari target. Penyaring aktif tetap bertahan saat berpindah layar dan kembali.
  • Layar detail buku menampilkan riwayat sesi bacanya.
  • Data bertahan setelah aplikasi ditutup.
  • Menghapus buku juga menghapus seluruh sesi miliknya, dan statistik ikut menyesuaikan tanpa perlu memuat ulang layar.

Requirement teknis

  • Pilih satu: Riverpod atau BLoC/Cubit. Jangan campur keduanya dalam proyek yang sama — tujuannya menguasai satu pola sampai tuntas.
  • Tidak ada setState untuk state yang dipakai lebih dari satu widget.setState hanya boleh untuk hal murni lokal seperti membuka-tutup panel.
  • Struktur berlapis yang jelas: data/ (model dan repository), lapisan logika (notifier atau BLoC), dan presentation/ (widget). Ketergantungan hanya mengarah ke bawah.
  • Lapisan logika tidak boleh mengimpor package:flutter/material.dart. Kalau kamu merasa membutuhkannya, ada logika UI yang bocor ke sana.
  • Statistik diekspos sebagai state turunan — provider yang mengawasi provider lain, atau Cubit yang mendengarkan repository. Bukan variabel yang disinkronkan manual.
  • State selalu diganti dengan objek baru (copyWith, [...list, item]), tidak pernah diubah di tempat.
  • Kalau memilih BLoC: state kustom memakai Equatable dengan seluruh properti masuk ke props. Kalau memilih Riverpod: efek samping seperti snackbar lewat ref.listen, bukan di dalam build.
  • Minimal tiga unit test untuk logika state, dijalankan tanpa membangun widget. Setidaknya satu di antaranya harus menguji perhitungan statistik turunan, dan satu lagi menguji bahwa copyWith tidak diam-diam menghapus properti lain.

Checklist penerimaan

  • flutter test lolos seluruhnya.
  • Menambah sesi baca di layar detail langsung mengubah angka di layar statistik, tanpa menutup dan membuka ulang layar itu.
  • Mencari kata setState di seluruh proyek tidak menemukan satu pun yang mengelola data buku atau sesi.
  • Mencari import 'package:flutter/material.dart' di dalam folder lapisan logika tidak menemukan hasil.
  • Kamu bisa menjelaskan, dalam satu kalimat, kenapa daftar tersaring bukan state yang disimpan.

Tantangan ekstra (opsional)

  • Tambahkan persistensi otomatis dengan HydratedBloc (Bab 25) — termasuk mengembalikan null dari toJson untuk keadaan memuat dan gagal, sehingga aplikasi langsung menampilkan data terakhir tanpa layar kosong.
  • Pasang BlocObserver (atau ProviderObserver untuk Riverpod) dan amati seluruh perubahan state di konsol selama satu sesi pemakaian penuh. Kamu akan menemukan setidaknya satu pembaruan yang terjadi lebih sering dari yang kamu kira.
  • Tulis ulang lapisan logikanya dengan pustaka yang satunya lagi, tanpa menyentuh folder data/ sama sekali. Kalau lapisanmu benar-benar bersih, ini seharusnya mungkin.

Terjebak?

Kebuntuan di proyek ini hampir selalu berbentuk sama: UI tidak diperbarui padahal datanya sudah berubah. Penyebabnya biasanya satu dari tiga hal — state diubah di tempat alih-alih diganti dengan objek baru, props pada Equatable tidak lengkap, atau notifyListeners() yang tidak dipanggil. Baca ulang Bab 20 atau Bab 18 sesuai pilihan pustakamu. Kalau masih macet di satu bagian spesifik, itu wajar: proyek ini memang dirancang untuk memaksamu mengingat kembali konsep yang sudah dibahas, bukan menghafalnya.

Rangkuman pembelajaran pribadi, disusun ulang dari beberapa kursus Flutter.