statesetStaterebuildMenengah4 mnt baca

setState Lebih Dalam

Kapan rebuild terjadi, apa yang dibangun ulang, dan jebakan umum.

Kenapa setState Ada?

Flutter memakai UI deklaratif: kamu cukup mendeskripsikan "tampilan untuk state saat ini", dan framework yang mengurus cara mengubah piksel di layar. Masalahnya, dunia nyata itu imperatif: tombol ditekan, data API datang, timer berdetak. setState adalah jembatan di antara keduanya. Kamu ubah variabel seperti biasa, lalu panggil setState, dan Flutter membangun ulang tampilan dari state terbaru. Tanpa mekanisme ini, kamu harus memegang referensi tiap Text dan Container lalu mengubahnya manual, seperti era findViewById yang melelahkan itu.

Apa yang Terjadi Saat Kamu Memanggilnya

setState tidak langsung membangun ulang widget saat itu juga. Ia hanya menandai element sebagai dirty, lalu framework menjadwalkan satu build ulang di frame berikutnya. Konsekuensinya dua hal. Pertama, memanggil setState sepuluh kali dalam satu event handler hanya menghasilkan satu rebuild, jadi tidak perlu takut memanggilnya berkali-kali. Kedua, memanggil setState di dalam build dilarang keras: build sedang berjalan, menandai dirinya dirty lagi sama dengan meminta loop tak berujung, dan Flutter akan melempar error.

dart
int _hitung = 0;

void _tambah() {
  setState(() {
    _hitung++; // ubah state di dalam callback
  });
}

Callback di dalam setState sebaiknya hanya berisi perubahan state, bukan logika berat. Sorting sepuluh ribu item atau parsing JSON besar kerjakan dulu di luar, hasilnya yang dimasukkan ke state.

dart
Future<void> _muatProduk() async {
  setState(() => _produk = null); // tampilkan loading
  final data = await api.ambilProduk(); // kerja berat di luar setState
  if (!mounted) return; // pengguna mungkin sudah menutup halaman
  setState(() {
    _produk = data;
  });
}

Tiga Jebakan Paling Umum

1. setState setelah dispose. Ini crash paling klasik: kamu memanggil API, pengguna menutup halaman sebelum respons datang, lalu callback async memanggil setState pada State yang sudah mati. Solusinya selalu cek mounted sebelum setState di callback async, seperti contoh di atas.

2. Seluruh subtree ikut rebuild. setState membangun ulang seluruh subtree dari State tersebut, bukan satu widget kecil. Kalau state counter tinggal di halaman raksasa, seluruh halaman ikut dibangun ulang. Solusinya: pecah UI jadi widget kecil, dan bubuhkan const pada bagian yang statis supaya framework bisa melewatkannya.

dart
// Hanya CounterWidget yang rebuild, HeaderRumit dilewati karena const
class HalamanBesar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const Column(
      children: [
        HeaderRumit(),
        CounterWidget(),
      ],
    );
  }
}

3. State ditaruh terlalu tinggi. Kalau state counter ditaruh di root aplikasi, tiap klik tombol membangun ulang segalanya. Aturan praktisnya: taruh state serendah mungkin, sedekat mungkin dengan widget yang memakainya.

Kapan Harus Pindah ke State Management

setState sempurna untuk state lokal satu layar: counter, isi form, tab aktif, status loading. Tapi untuk state yang dipakai banyak layar (keranjang belanja, data login, tema aplikasi), setState memaksamu mengangkat state ke ancestor tertinggi dan rebuild jadi mahal serta sulit dilacak. Di titik itu, Riverpod, Provider, atau BLoC memberi struktur yang lebih sehat: state terpusat, rebuild selektif, dan logika terpisah dari UI.

Catatan teknis: Di dalam initState kamu tidak perlu memanggil setState, karena build pertama pasti berjalan setelahnya. setState hanya valid saat State sedang mounted, yaitu antara initState dan dispose.

Tantangan

Pecah widget counter

Refactor halaman counter-mu: pisahkan tampilan angka menjadi widget TampilanAngka (Stateless) dan tombol-tombol menjadi TombolCounter (Stateless dengan callback). State hanya tinggal di parent.