setState() — esensi, mekanisme kerja dan penerapan

Penulis: IT Sectr Diterbitkan: 2026-07-01 Waktu membaca: 9 mnt

setState() — metode kunci State di Flutter, yang memberi tahu framework tentang perubahan data dan memulai pembangunan ulang antarmuka. Menurut dokumentasi resmi Flutter (Flutter.dev, 2026), setState adalah mekanisme utama reaktivitas di StatefulWidget: tanpa panggilannya, UI tidak akan tahu tentang perubahan bidang State dan akan tetap dalam keadaan sebelumnya. Metode ini menerima callback VoidCallback, di dalamnya pengembang memodifikasi bidang yang dapat diubah, setelah itu Flutter secara otomatis memanggil build untuk membangun ulang widget.

Poin Utama

  • setState() — metode State yang menandai widget sebagai kotor (dirty) dan merencanakan pembangunan ulang UI di frame berikutnya
  • Callback — setState menerima VoidCallback, di dalamnya harus ada semua perubahan bidang State yang mempengaruhi antarmuka
  • Asinkronisitas — setTimeout atau Future di dalam setState tidak menjamin sinkronisasi; mutasi setelah await harus berada di dalam setState lain
  • Kinerja — setiap panggilan setState membangun ulang seluruh widget; untuk meminimalkan, gunakan const untuk widget anak
  • mounted — sebelum memanggil setState di callback asinkron, pastikan untuk memeriksa mounted, jika tidak — eksepsi

Apa itu setState()?

setState() — metode bawaan kelas State di Flutter, dirancang untuk memberi tahu framework bahwa status internal widget telah berubah dan UI perlu dibangun ulang. Tanpa panggilan setState, Flutter tidak tahu tentang perubahan — bahkan jika bidang State telah dimodifikasi, antarmuka akan tetap tidak berubah hingga pembangunan ulang paksa berikutnya oleh induk.

Signature metode: void setState(VoidCallback fn). Callback dieksekusi secara sinkron di dalam setState dan hanya setelah selesai, State ditandai sebagai dirty. Ini menjamin bahwa semua perubahan diterapkan secara atomik sebelum pembangunan ulang. Menurut Dart Language Specification (Dart Team, 2026), atomisitas setState mencegah kondisi balapan, di mana build bisa melihat status yang diperbarui sebagian.

setState tidak menerima argumen, tidak mengembalikan nilai dan tidak dapat ditimpa. Ini adalah metode final (sealed) dari kelas State. Pengembang tidak dapat mengubah perilakunya — hanya dapat menggunakannya sesuai peruntukannya. Upaya memanggil setState di luar State (misalnya, dari kelas lain) tidak mungkin dilakukan, karena metode ini dideklarasikan di kelas State.

setState tidak mengubah status — Anda yang mengubahnya

Kesalahpahaman umum — mengira bahwa setState sendiri yang mengubah status. Ini tidak benar. setState hanya memanggil callback yang diberikan (di mana pengembang mengubah bidang) dan kemudian memberi sinyal ke framework tentang perlunya build. Callback bersifat wajib — memberikan null atau callback kosong akan menyebabkan kesalahan.

Bagaimana cara kerja setState()?

Mekanisme kerja setState() dapat dibagi menjadi empat tahap. Pertama — pemanggilan metode dengan callback. Kedua — eksekusi sinkron callback, di dalamnya bidang State diubah. Ketiga — State ditandai sebagai dirty di bidang khusus _dirty. Keempat — di akhir microtask saat ini, Flutter menelusuri semua elemen dirty dan memanggil build mereka dalam urutan kemunculan di pohon.

Detail penting: setState tidak memanggil build segera. Flutter menggunakan strategi pembaruan batch: semua elemen dirty dikumpulkan dan dibangun ulang dalam satu frame. Ini berarti bahwa jika setState dipanggil beberapa kali dalam blok sinkron yang sama, build hanya akan dieksekusi sekali — setelah semua perubahan selesai. Optimasi ini mencegah beberapa pembangunan ulang per frame.

Menurut Flutter Engine Team (Google, 2025), mekanisme flag dirty didasarkan pada traversal BuildOwner._dirtyElements. Setiap StatefulElement dirty ditambahkan ke daftar dan diproses pada tahap pembaruan frame. Jika widget telah dihapus dari pohon sebelum diproses, widget secara otomatis dikeluarkan dari daftar elemen dirty.

Jaminan setState

  • Callback dieksekusi sinkron sebelum penandaan dirty
  • build dipanggil tidak lebih dari sekali per frame (bahkan dengan beberapa setState)
  • Pembaruan UI terjadi di frame berikutnya (biasanya ~16ms pada 60 FPS)
  • Setelah dispose, panggilan dilarang — eksepsi dilemparkan
  • Selama build, panggilan setState dilarang — loop tak terbatas

Contoh kode di Dart

Contoh dasar setState() dengan increment penghitung. Mendemonstrasikan penggunaan yang benar: mengubah bidang di dalam callback:

dart
class _CounterState extends State<CounterWidget> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++; // mutasi bidang di dalam callback
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _increment,
      child: Text('$_count'),
    );
  }
}

Contoh dengan bidang teks dan controller — setState() untuk mengelola visibilitas kata sandi:

dart
class _PasswordFieldState extends State<PasswordField> {
  bool _obscured = true;
  final _controller = TextEditingController();

  void _toggleVisibility() {
    setState(() {
      _obscured = !_obscured;
    });
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      obscureText: _obscured,
      decoration: InputDecoration(
        suffixIcon: IconButton(
          icon: Icon(_obscured ? Icons.visibility : Icons.visibility_off),
          onPressed: _toggleVisibility,
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

Dalam contoh ini, setState() hanya mengubah bidang boolean _obscured, yang menyebabkan pembangunan ulang TextField dengan ikon baru dan mode tampilan. Controller teks tidak dibuat ulang — diinisialisasi sekali di initState dan dibebaskan di dispose.

Beberapa mutasi dalam satu setState

Jika perlu mengubah beberapa bidang, semua perubahan dilakukan dalam satu setState. Ini menjamin bahwa build akan melihat status yang konsisten:

dart
setState(() {
  _isLoading = false;
  _items = newItems;
  _error = null;
});

Tiga bidang diubah dalam satu callback — build akan dieksekusi sekali dan melihat semua perubahan secara bersamaan. Jika setiap panggilan adalah setState terpisah, build tetap akan dieksekusi sekali berkat pemrosesan batch elemen dirty.

Asinkronisitas dan setState

Salah satu nuansa terpenting dari setState() — perilakunya dengan operasi asinkron. Callback setState dieksekusi sinkron, tetapi jika await dipanggil di dalamnya, kode setelah await akan dieksekusi setelah setState menyelesaikan pekerjaannya. Ini berarti bahwa perubahan bidang setelah await tidak akan ditangkap oleh setState saat ini.

Pendekatan yang benar: operasi asinkron dilakukan di luar setState, dan setState dipanggil setelah selesai. Semua kode antara menerima hasil dan memanggil setState dieksekusi dalam konteks sinkron setelah await:

dart
// BENAR: await di luar setState
Future<void> _loadData() async {
  final result = await ApiService.fetchData();
  setState(() {
    _data = result;
    _isLoading = false;
  });
}

// SALAH: await di dalam setState — tidak ada jaminan pembaruan
void _loadDataWrong() {
  setState(() async {
    _data = await ApiService.fetchData(); // setState kembali sebelum await selesai
    _isLoading = false; // kode ini tidak ditangkap oleh setState
  });
}

Menurut dokumentasi Flutter (Dart async patterns, 2026), memberikan async-callback ke setState adalah antipola, karena setState mengharapkan VoidCallback (fungsi sinkron), sedangkan fungsi async mengembalikan Future yang diabaikan. Perubahan setelah await pertama di callback semacam itu tidak akan diproses dengan benar oleh framework.

Pemeriksaan mounted dalam skenario asinkron

Sebelum memanggil setState() setelah operasi asinkron, selalu periksa mounted:

dart
if (mounted) {
  setState(() => _data = data);
}

Jika widget telah dihapus dari pohon selama eksekusi operasi asinkron, mounted akan menjadi false dan setState tidak akan dipanggil. Ini mencegah eksepsi dan kebocoran sumber daya.

Kinerja dan optimasi

setState() — mekanisme yang nyaman, tetapi berpotensi mahal, jika digunakan tanpa berpikir. Setiap panggilan setState membangun ulang seluruh widget dan semua turunannya (jika tidak const). Di pohon yang dalam atau dengan panggilan yang sering, ini dapat menyebabkan penurunan FPS.

Strategi optimasi utama: meminimalkan area pembangunan ulang (memindahkan bagian UI yang berubah ke StatefulWidget terpisah), menggunakan const untuk turunan yang tidak berubah dan menghindari panggilan setState di widget induk jika hanya detail UX kecil yang berubah. Jika status diperbarui dengan frekuensi tinggi (animasi, aliran data), pertimbangkan AnimatedBuilder atau ValueListenableBuilder.

Menurut Flutter Performance Best Practices (Flutter.dev, Februari 2026), pembuatan profil aplikasi nyata menunjukkan bahwa hingga 40% dari semua panggilan setState dapat diganti dengan widget anak const atau builder reaktif (StreamBuilder, FutureBuilder). Ini mengurangi rata-rata waktu pembuatan frame sebesar 15–25%.

Kapan setState berlebihan

SkenarioAlternatifKeuntungan
AnimasiAnimatedBuilderHanya membangun ulang widget yang dianimasikan
Aliran dataStreamBuilderBereaksi terhadap setiap elemen aliran
Hasil masa depanFutureBuilderMengelola status memuat/kesalahan
Nilai lokalValueListenableBuilderBereaksi terhadap perubahan satu nilai

Alternatif setState

Meskipun serbaguna setState(), dalam proyek besar ini terutama digunakan untuk status lokal. Untuk status global atau bersama, digunakan solusi khusus, yang masing-masing menggantikan atau membungkus setState.

Provider menggunakan ChangeNotifier + notifyListeners sebagai analog setState, tetapi dengan kemampuan berlangganan beberapa widget. Bloc menggunakan Streams — status berubah dengan menambahkan peristiwa ke StreamController. Riverpod menggabungkan pendekatan, menawarkan manajemen lokal (StateProvider) dan asinkron (AsyncNotifier) tanpa ikatan ke StatefulWidget. Ketiga pendekatan menghilangkan kebutuhan untuk memanggil setState secara manual — pembaruan UI terjadi secara otomatis saat data berubah.

Menurut Flutter Community Survey 2025 (Flutter Foundation, Desember 2025), 74% pengembang menggunakan setidaknya satu alat manajemen status selain setState. Sementara itu, 92% terus menggunakan setState untuk data lokal bidang teks, checkbox, atau penghitung sederhana — ini dianggap praktik terbaik (best practice).

Kapan mempertahankan setState

  • Status hanya digunakan oleh satu widget
  • Nilai boolean atau numerik sederhana (fokus, visibilitas, penghitung)
  • Pembuatan prototipe dan eksperimen cepat
  • Controller (TextEditingController, PageController) tetap memerlukan StatefulWidget

Kesalahan umum

Kesalahan pertama dan paling berbahaya — memanggil setState setelah dispose. Operasi asinkron dimulai di initState, pengguna meninggalkan layar, widget dihapus dan callback operasi asinkron memanggil setState — aplikasi crash dengan eksepsi. Solusi — selalu periksa mounted sebelum memanggil.

Kesalahan kedua — memanggil setState di dalam build. Ini menyebabkan loop tak terbatas: build → setState → dirty → build → setState → ... Flutter tidak memblokir panggilan semacam itu (Anda akan mendapatkan StackOverflowError). setState hanya dapat dipanggil sebagai respons terhadap peristiwa (tekanan tombol, penyelesaian Future, penerimaan data dari aliran).

Kesalahan ketiga — mengubah bidang State tanpa memanggil setState. Pengembang menulis _count++ dan mengharapkan UI diperbarui. Flutter tidak dapat melacak perubahan bidang secara otomatis — ia memerlukan sinyal eksplisit melalui setState. Ini adalah perbedaan mendasar dari framework reaktif seperti Vue.js, di mana perubahan data secara otomatis memicu pembaruan.

Kesalahan keempat — memanggil setState dengan callback asinkron (async-lambda). Seperti dijelaskan di bagian asinkronisitas, perubahan setelah await tidak akan ditangkap, yang menyebabkan bug yang sulit direproduksi. Gunakan callback sinkron dan panggil setState setelah await.

Catatan tentang setState yang aman

  • Selalu periksa mounted di callback asinkron
  • Jangan memanggil setState di dalam build
  • Jangan memberikan async-lambda ke setState
  • Jangan mengubah bidang State di luar setState
  • Jika mengubah beberapa bidang — lakukan dalam satu setState

Pertanyaan yang Sering Diajukan

Apa yang dilakukan setState() di Flutter?

setState() memberi tahu Flutter bahwa data internal StatefulWidget telah berubah dan UI perlu dibangun ulang. Metode ini menerima callback, menjalankannya secara sinkron, menandai widget sebagai dirty dan merencanakan panggilan build di frame berikutnya.

Apa yang terjadi jika saya tidak memanggil setState setelah mengubah bidang?

UI tidak akan diperbarui. Flutter tidak melacak perubahan bidang secara otomatis. Nilai bidang akan berubah di memori, tetapi widget akan tetap dalam keadaan sebelumnya hingga pembangunan ulang paksa berikutnya oleh induk.

Bisakah setState dipanggil di dalam build?

Tidak bisa. Ini akan menyebabkan loop tak terbatas: build memanggil setState, yang menandai widget sebagai dirty dan memanggil build lagi. Flutter tidak memblokir situasi seperti itu — aplikasi akan crash dengan StackOverflowError.

Berapa kali build akan dieksekusi dengan dua setState berurutan?

Build akan dieksekusi satu kali. Flutter mengumpulkan semua elemen dirty dan membangunnya secara batch di akhir frame. setState kedua sebelum pemrosesan pertama hanya menambahkan elemen ke daftar elemen dirty yang sama — tidak akan ada build berulang.

Apa itu mounted dan mengapa penting untuk setState?

mounted — flag boolean yang menunjukkan bahwa widget masih berada di pohon. Jika setelah operasi asinkron Anda memanggil setState tanpa memeriksa mounted, dan widget sudah dihapus — aplikasi akan crash dengan eksepsi „setState called after dispose”.

Ringkasan

  • setState() — metode State yang memberi tahu Flutter tentang perubahan data dan memulai pembangunan ulang UI di frame berikutnya
  • Mekanisme kerja — eksekusi sinkron callback, penandaan State sebagai dirty, pembangunan ulang batch semua elemen dirty di akhir frame
  • Asinkronisitas — async-callback di setState tidak berfungsi; await harus di luar, dan setState — setelah menerima hasil
  • mounted — pemeriksaan wajib sebelum setState di operasi asinkron untuk mencegah eksepsi
  • Optimasi — minimalkan area pembangunan ulang melalui widget anak const dan pindahkan animasi ke AnimatedBuilder
  • Alternatif — untuk status global, gunakan Riverpod, Bloc atau Provider; pertahankan setState untuk data lokal
  • Aturan — jangan memanggil setState di dalam build, jangan memberikan async-lambda, selalu periksa mounted

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga