Episode ini membuka dunia state management: mengelola state lokal dengan setState, teknik lifting state up dan prop drilling, pengenalan pattern Provider dan InheritedWidget, serta best practice untuk state skala kecil sebelum melompat ke arsitektur yang lebih besar.

Semakin besar aplikasi, semakin banyak data yang harus dibagi antar widget. State management menjawab pertanyaan sederhana tapi krusial: di mana data tinggal dan bagaimana perubahan data memicu update UI. Episode 6 adalah pengantar state management yang terkontrol: mulai dari setState() untuk state lokal, lalu lifting state up dan prop drilling untuk membagi state antar widget, pengenalan InheritedWidget dan Provider sebagai pattern resmi, serta best practice untuk aplikasi skala kecil sebelum kalian naik ke arsitektur lanjutan di episode 10.
setState() adalah mekanisme paling dasar. Cocok ketika state hanya dipakai oleh satu widget dan keturunannya:
class _CounterState extends State<CounterWidget> {
int _nilai = 0;
void _tambah() {
setState(() {
_nilai++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Nilai: $_nilai'),
ElevatedButton(
onPressed: _tambah,
child: const Text('Tambah'),
),
],
);
}
}setState memberi tahu framework bahwa state berubah sehingga build dijalankan ulang. Prinsipnya: jangan mutasi state di luar setState, karena UI tidak akan diperbarui.
Saat dua widget saudara harus berbagi data, naikkan state ke parent bersama:
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _aktif = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
value: _aktif,
onChanged: (nilai) => setState(() => _aktif = nilai),
),
ChildWidget(aktif: _aktif),
],
);
}
}_aktif tinggal di parent, dan ChildWidget menerimanya sebagai parameter. Teknik ini disebut lifting state up. Callback diturunkan bersama data agar child bisa melaporkan perubahan ke parent.
Saat pohon widget semakin dalam, meneruskan data lewat parameter berulang-ulang disebut prop drilling. Pada kedalaman dua atau tiga level masih wajar, tetapi untuk aplikasi besar, cara ini membuat kode berisik dan sulit dirawat. Di situlah pattern di bagian berikutnya berperan.
InheritedWidget adalah mekanisme Flutter untuk membagikan data ke seluruh subtree tanpa meneruskan parameter satu per satu. Data diakses lewat context.dependOnInheritedWidgetOfExactType.
Walaupun kalian jarang menulis InheritedWidget langsung, memahaminya penting karena Provider dibangun di atasnya:
BuildContext context;
final data = context.dependOnInheritedWidgetOfExactType<DataProvider>();context.dependOnInheritedWidgetOfExactType<T>() mencari InheritedWidget terdekat dan me-subscribe widget ini terhadap perubahannya.
Menulis InheritedWidget manual rawan error. Solusinya: package Provider yang membungkus InheritedWidget dengan API sederhana:
flutter pub add providerSetelah terpasang, bungkus aplikasi dengan ChangeNotifierProvider:
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: const MaterialApp(home: CounterScreen()),
)ChangeNotifierProvider(create: ...) membuat instance CounterModel dan membagikannya ke seluruh pohon di bawahnya. CounterModel meng-extend ChangeNotifier dan memanggil notifyListeners() saat data berubah.
class CounterModel extends ChangeNotifier {
int _nilai = 0;
int get nilai => _nilai;
void tambah() {
_nilai++;
notifyListeners();
}
}
class CounterScreen extends StatelessWidget {
const CounterScreen({super.key});
@override
Widget build(BuildContext context) {
final model = context.watch<CounterModel>();
return Scaffold(
body: Center(child: Text('Nilai: ${model.nilai}')),
floatingActionButton: FloatingActionButton(
onPressed: model.tambah,
child: const Icon(Icons.add),
),
);
}
}context.watch<CounterModel> membuat widget rebuild ketika model berubah. Aturan kuncinya: pakai watch untuk read yang harus memicu rebuild, dan read untuk aksi tanpa rebuild.
Untuk aplikasi kecil sampai menengah, aturan ini sudah cukup:
setState.flutter pub depsflutter pub deps menampilkan pohon dependency project — berguna untuk memeriksa paket apa saja yang sudah terpasang sebelum menambah package lain.
Pilih satu pola dan terapkan secara konsisten di seluruh project. Di skala kecil, campuran setState dan Provider untuk kasus yang tepat justru lebih mudah dirawat daripada memaksakan satu arsitektur untuk segala hal. Episode 10 akan membahas pola lanjutan ketika aplikasi tumbuh besar.
Inti yang harus dibawa pulang:
setState cukup untuk state yang hanya dipakai satu widget.context.watch untuk rebuild; context.read untuk aksi tanpa rebuild.Di episode 7 selanjutnya kita membahas theming dan styling — ThemeData dan material design theming, custom fonts, colors, dan typography, dukungan dark mode dan adaptive UI, serta styling widget, decoration, dan responsive design. Aplikasi kalian mulai tampil profesional.