ValueNotifier & ValueListenableBuilder
State ringan tanpa setState: cocok untuk nilai tunggal.
Kenapa ValueNotifier Ada?
Bayangkan kamu punya halaman berisi ratusan widget, dan hanya satu angka kecil (misalnya jumlah item di keranjang) yang sering berubah. Kalau pakai setState, Flutter memanggil ulang seluruh method build milik widget itu. Itu berat dan boros, apalagi kalau halamanmu kompleks.
ValueNotifier lahir untuk kasus ini: state untuk satu nilai tunggal yang bisa berubah, dengan cara memberi tahu listener setiap nilai diganti. Tidak perlu StatefulWidget untuk logikanya, cukup satu objek kecil.
Contoh Sederhana: Counter
final _counter = ValueNotifier<int>(0);
// mengubah nilai (cukup assign ke .value):
_counter.value++;
// menampilkan nilai:
ValueListenableBuilder<int>(
valueListenable: _counter,
builder: (context, nilai, child) {
return Text('Angka: $nilai');
},
)Setiap _counter.value diganti, ValueNotifier memanggil notifyListeners(), dan ValueListenableBuilder me-rebuild hanya bagian builder-nya, bukan seluruh halaman.
Trik Performa: Parameter child
Widget di dalam builder yang tidak bergantung pada nilai sebaiknya dipindah ke parameter child, agar tidak ikut rebuild:
ValueListenableBuilder<int>(
valueListenable: _counter,
child: const Icon(Icons.shopping_cart), // statis, tidak ikut rebuild
builder: (context, nilai, child) {
return Row(children: [
child!, // ikon dipakai ulang
Text('$nilai'), // hanya teks ini yang rebuild
]);
},
)Contoh Nyata: Toggle Favorit
final _suka = ValueNotifier<bool>(false);
ValueListenableBuilder<bool>(
valueListenable: _suka,
builder: (context, suka, _) {
return IconButton(
icon: Icon(
suka ? Icons.favorite : Icons.favorite_border,
color: suka ? Colors.red : Colors.grey,
),
onPressed: () => _suka.value = !suka,
);
},
)Catatan teknis:
ValueNotifierbisa dipakai juga di luar widget (misalnya di service sederhana) selama widget mendengarkannya lewatValueListenableBuilderatauaddListener. Tapi untuk state yang dipakai banyak layar, pertimbangkanProvideratauRiverpodagar struktur datanya jelas.
Edge Case: Nilai Sama Tidak Memicu Rebuild
ValueNotifier hanya memanggil notifyListeners() jika nilai baru tidak sama dengan nilai lama (perbandingan pakai ==). Jadi _counter.value = 5 dua kali berturut-turut hanya me-rebuild sekali.
Hati-hati dengan objek mutable:
final _daftar = ValueNotifier<List<String>>(['a']);
_daftar.value.add('b'); // TIDAK rebuild! Objeknya sama, isinya berubah.
_daftar.value = [..._daftar.value, 'b']; // OK: assign objek baruKalau memang ingin memaksa rebuild setelah mutasi langsung, panggil _daftar.notifyListeners() secara manual.
Kapan JANGAN Pakai ValueNotifier
- State kompleks dengan banyak field yang saling terkait (form, keranjang belanja penuh). Pakai model state yang lebih terstruktur agar logika tidak tercecer.
- State lintas layar yang dalam (theme, sesi login). ValueNotifier tidak punya mekanisme scoping; pakai
InheritedWidget,Provider, atauRiverpod. - State yang butuh async (loading dari API). ValueNotifier tidak menangani future/stream; pakai
FutureBuilder/StreamBuilder.
Jangan Lupa Dispose
ValueNotifier yang kamu buat manual menahan listener di memori. Selalu dispose di StatefulWidget yang memilikinya:
@override
void dispose() {
_counter.dispose();
super.dispose();
}Lupa dispose adalah memory leak kecil yang menumpuk: setiap buka-tutup halaman menambah listener yang tidak pernah dibersihkan.
Tantangan
Toggle tanpa setState
Buat toggle favorit (ikon hati merah/abu) memakai ValueNotifier<bool>, tanpa StatefulWidget untuk logikanya.