Belajar Flutter - State Management Dasar
Episode 6 of 23

Belajar Flutter - State Management Dasar

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

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.

Mengelola State Lokal dengan setState

State di Dalam Satu Widget

setState() adalah mekanisme paling dasar. Cocok ketika state hanya dipakai oleh satu widget dan keturunannya:

Counter dengan setState
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.

Lifting State Up dan Prop Drilling

Memindahkan State ke Ancestor

Saat dua widget saudara harus berbagi data, naikkan state ke parent bersama:

State diangkat ke parent
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.

Prop Drilling: Harga dari Lifting

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.

Pattern: InheritedWidget dan Provider

InheritedWidget

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:

Konsep akses data lewat context
BuildContext context;
 
final data = context.dependOnInheritedWidgetOfExactType<DataProvider>();

context.dependOnInheritedWidgetOfExactType<T>() mencari InheritedWidget terdekat dan me-subscribe widget ini terhadap perubahannya.

Provider sebagai Abstraksi Resmi

Menulis InheritedWidget manual rawan error. Solusinya: package Provider yang membungkus InheritedWidget dengan API sederhana:

Menambahkan provider ke project
flutter pub add provider

Setelah terpasang, bungkus aplikasi dengan ChangeNotifierProvider:

Menyediakan state dengan provider
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.

Mengonsumsi state dengan watch
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.

Best Practice untuk Small-Scale State

Kapan Menggunakan Apa

Untuk aplikasi kecil sampai menengah, aturan ini sudah cukup:

  • State yang hanya dipakai satu widget: pakai setState.
  • State yang dibagi beberapa widget terdekat: lifting state up.
  • State yang dipakai lintas layar atau dalam subtree dalam: Provider.
  • Hindari menyimpan data yang tidak berubah di dalam state.
Verifikasi dependency terpasang
flutter pub deps

flutter pub deps menampilkan pohon dependency project — berguna untuk memeriksa paket apa saja yang sudah terpasang sebelum menambah package lain.

Konsistensi Lebih Penting dari Kecanggihan

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.

Penutup

Inti yang harus dibawa pulang:

  • setState cukup untuk state yang hanya dipakai satu widget.
  • Lifting state up: naikkan state ke ancestor bersama saat beberapa widget berbagi data.
  • Prop drilling menjadi mahal pada pohon yang dalam — waktunya pindah pattern.
  • Provider dibangun di atas InheritedWidget dan menjadi abstraksi resmi yang aman.
  • context.watch untuk rebuild; context.read untuk aksi tanpa rebuild.
  • Untuk skala kecil, konsistensi pola lebih penting daripada kecanggihan.

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.

Belajar Flutter - State Management Dasar | Belajar Flutter