setState() — essentie, werkingsmechanisme en toepassing

Auteur: IT Sectr Gepubliceerd: 2026-07-01 Leestijd: 9 min

setState() — de belangrijkste methode van State in Flutter, die het framework op de hoogte stelt van gegevenswijziging en de herbouw van de interface start. Volgens de officiële Flutter-documentatie (Flutter.dev, 2026) is setState het belangrijkste mechanisme van reactiviteit in StatefulWidget: zonder aanroep ervan zal de UI niets weten over wijzigingen in de State-velden en in de vorige toestand blijven. De methode accepteert een VoidCallback, waarbinnen de ontwikkelaar de veranderlijke velden wijzigt, waarna Flutter automatisch build aanroept om de widget te herbouwen.

Belangrijkste punten

  • setState() — State-methode die de widget als dirty markeert en de herbouw van UI in het volgende frame plant
  • Callback — setState accepteert een VoidCallback, waarbinnen alle wijzigingen van State-velden die de interface beïnvloeden moeten plaatsvinden
  • Asynchroniteit — setTimeout of Future binnen setState garanderen geen synchronisatie; mutaties na await moeten binnen een andere setState zijn
  • Prestatie — elke setState-aanroep herbouwt de hele widget; minimaliseer door const te gebruiken voor kind-widgets
  • mounted — controleer vóór het aanroepen van setState in asynchrone callbacks altijd mounted, anders — uitzondering

Wat is setState()?

setState() — een ingebouwde methode van de klasse State in Flutter, bedoeld om het framework te melden dat de interne toestand van de widget is gewijzigd en herbouw van de UI nodig is. Zonder setState-aanroep weet Flutter niets van wijzigingen — zelfs als State-velden zijn gewijzigd, blijft de interface ongewijzigd tot de volgende gedwongen herbouw door de ouder.

Handtekening van de methode: void setState(VoidCallback fn). De callback wordt synchroon uitgevoerd binnen setState en pas na voltooiing wordt State als dirty gemarkeerd. Dit garandeert dat alle wijzigingen atomair worden toegepast vóór herbouw. Volgens de Dart Language Specification (Dart Team, 2026) voorkomt de atomiciteit van setState race-condities, waarbij build een gedeeltelijk bijgewerkte toestand zou kunnen zien.

setState accepteert geen argumenten, retourneert geen waarde en kan niet worden overschreven. Het is een finale (sealed) methode van de klasse State. De ontwikkelaar kan het gedrag niet wijzigen — het kan alleen volgens bestemming worden gebruikt. Een poging om setState buiten State aan te roepen (bijvoorbeeld vanuit een andere klasse) is onmogelijk, omdat de methode in de klasse State is gedeclareerd.

setState verandert de toestand niet — u verandert deze

Een veelvoorkomende misvatting — denken dat setState zelf de toestand wijzigt. Dit is niet waar. setState roept alleen de doorgegeven callback aan (waarin de ontwikkelaar de velden wijzigt) en signaleert vervolgens het framework over de noodzaak van build. De callback is verplicht — het doorgeven van null of een lege callback veroorzaakt een fout.

Hoe werkt setState()?

Het werkingsmechanisme van setState() kan in vier fasen worden verdeeld. Eerste — aanroep van de methode met callback. Tweede — synchrone uitvoering van de callback, waarbinnen de State-velden worden gewijzigd. Derde — State wordt gemarkeerd als dirty in het speciale veld _dirty. Vierde — aan het einde van de huidige microtask doorloopt Flutter alle dirty-elementen en roept hun build aan in volgorde van voorkomen in de boom.

Een belangrijk detail: setState roept build niet onmiddellijk aan. Flutter gebruikt een batch-updatestrategie: alle dirty-elementen worden verzameld en in één frame herbouwd. Dit betekent dat als setState meerdere keren binnen hetzelfde synchrone blok wordt aangeroepen, build slechts één keer wordt uitgevoerd — na voltooiing van alle wijzigingen. Deze optimalisatie voorkomt meerdere herbouwen per frame.

Volgens het Flutter Engine Team (Google, 2025) is het mechanisme van dirty-vlaggen gebaseerd op het doorlopen van BuildOwner._dirtyElements. Elk dirty StatefulElement wordt aan de lijst toegevoegd en verwerkt in de frame-updatefase. Als de widget vóór verwerking uit de boom is verwijderd, wordt deze automatisch uit de lijst van dirty-elementen uitgesloten.

Garanties van setState

  • De callback wordt synchroon uitgevoerd vóór dirty-markering
  • build wordt maximaal één keer per frame aangeroepen (zelfs bij meerdere setState)
  • UI-update vindt plaats in het volgende frame (meestal ~16ms bij 60 FPS)
  • Na dispose is aanroep verboden — er wordt een uitzondering gegooid
  • Tijdens build is setState-aanroep verboden — oneindige lus

Codevoorbeelden in Dart

Basisvoorbeeld van setState() met teller-increment. Demonstreert correct gebruik: veld wijzigen binnen de callback:

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

  void _increment() {
    setState(() {
      _count++; // veld muteren binnen callback
    });
  }

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

Voorbeeld met tekstveld en controller — setState() voor het beheren van wachtwoordzichtbaarheid:

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();
  }
}

In dit voorbeeld wijzigt setState() alleen het booleaanse veld _obscured, wat herbouw van TextField met een nieuw pictogram en weergavemodus veroorzaakt. De tekstcontroller wordt niet opnieuw aangemaakt — deze wordt eenmaal geïnitialiseerd in initState en vrijgegeven in dispose.

Meerdere mutaties in één setState

Als meerdere velden moeten worden gewijzigd, worden alle wijzigingen binnen één setState uitgevoerd. Dit garandeert dat build een consistente toestand ziet:

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

Drie velden worden in één callback gewijzigd — build wordt één keer uitgevoerd en ziet alle wijzigingen tegelijk. Als elke aanroep een afzonderlijke setState was geweest, zou build nog steeds eenmalig worden uitgevoerd dankzij batchverwerking van dirty-elementen.

Asynchroniteit en setState

Een van de belangrijkste nuances van setState() — het gedrag met asynchrone bewerkingen. De setState-callback wordt synchroon uitgevoerd, maar als er binnenin await wordt aangeroepen, wordt de code na await uitgevoerd nadat setState zijn werk heeft voltooid. Dit betekent dat wijzigingen van velden na await niet worden opgevangen door de huidige setState.

Juiste benadering: de asynchrone bewerking wordt buiten setState uitgevoerd en setState wordt na voltooiing aangeroepen. Alle code tussen het ontvangen van het resultaat en het aanroepen van setState wordt in synchrone context na await uitgevoerd:

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

// FOUT: await binnen setState — geen updategarantie
void _loadDataWrong() {
  setState(() async {
    _data = await ApiService.fetchData(); // setState keert terug vóór await voltooid is
    _isLoading = false; // deze code wordt niet opgevangen door setState
  });
}

Volgens Flutter-documentatie (Dart async patterns, 2026) is het doorgeven van een async-callback aan setState een antipatroon, omdat setState een VoidCallback (synchrone functie) verwacht, terwijl een async-functie een Future retourneert die wordt genegeerd. Wijzigingen na de eerste await in een dergelijke callback worden niet correct verwerkt door het framework.

mounted-controle in asynchrone scenario's

Controleer vóór het aanroepen van setState() na een asynchrone bewerking altijd mounted:

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

Als de widget tijdens de uitvoering van de asynchrone bewerking uit de boom is verwijderd, wordt mounted false en wordt setState niet aangeroepen. Dit voorkomt uitzonderingen en geheugenlekken.

Prestatie en optimalisatie

setState() — een handig maar potentieel duur mechanisme, als het gedachteloos wordt gebruikt. Elke setState-aanroep herbouwt de hele widget en al zijn afstammelingen (als ze niet const zijn). In diepe bomen of bij frequente aanroepen kan dit leiden tot FPS-dalingen.

Belangrijkste optimalisatiestrategieën: minimaliseer het herbouwgebied (haal variabele UI-delen naar aparte StatefulWidgets), gebruik const voor onveranderlijke afstammelingen en vermijd setState-aanroepen in ouder-widgets als slechts een klein UX-detail is gewijzigd. Als de toestand met hoge frequentie wordt bijgewerkt (animatie, gegevensstroom), overweeg dan AnimatedBuilder of ValueListenableBuilder.

Volgens Flutter Performance Best Practices (Flutter.dev, februari 2026) toont profilering van echte applicaties aan dat tot 40% van alle setState-aanroepen kan worden vervangen door const-kind-widgets of reactieve builders (StreamBuilder, FutureBuilder). Dit vermindert de gemiddelde framebouwtijd met 15–25%.

Wanneer setState overbodig is

ScenarioAlternatiefVoordeel
AnimatieAnimatedBuilderHerbouwt alleen de geanimeerde widget
GegevensstroomStreamBuilderReageert op elk element van de stroom
Toekomstig resultaatFutureBuilderBeheert laad-/fouttoestanden
Lokale waardeValueListenableBuilderReageert op wijzigingen van één waarde

Alternatieven voor setState

Ondanks de veelzijdigheid van setState() wordt het in grote projecten voornamelijk gebruikt voor lokale toestand. Voor globale of gedeelde toestand worden gespecialiseerde oplossingen gebruikt, die elk setState vervangen of inpakken.

Provider gebruikt ChangeNotifier + notifyListeners als analoog van setState, maar met de mogelijkheid van abonnement door meerdere widgets. Bloc gebruikt Streams — de toestand verandert door gebeurtenissen aan StreamController toe te voegen. Riverpod combineert benaderingen en biedt zowel lokaal (StateProvider) als asynchroon (AsyncNotifier) beheer zonder binding aan StatefulWidget. Alle drie benaderingen elimineren de noodzaak om handmatig setState aan te roepen — UI-update gebeurt automatisch bij gegevenswijziging.

Volgens Flutter Community Survey 2025 (Flutter Foundation, december 2025) gebruikt 74% van de ontwikkelaars ten minste één hulpmiddel voor toestandsbeheer naast setState. Tegelijkertijd blijft 92% setState gebruiken voor lokale gegevens van tekstveld, checkbox of eenvoudige teller — dit wordt beschouwd als best practice.

Wanneer setState behouden

  • Toestand wordt slechts door één widget gebruikt
  • Eenvoudige booleaanse of numerieke waarde (focus, zichtbaarheid, teller)
  • Prototyping en snelle experimenten
  • Controllers (TextEditingController, PageController) vereisen sowieso StatefulWidget

Veelvoorkomende fouten

De eerste en gevaarlijkste fout — setState aanroepen na dispose. Een asynchrone bewerking is gestart in initState, de gebruiker heeft het scherm verlaten, de widget is verwijderd en de callback van de asynchrone bewerking roept setState aan — de applicatie crasht met een uitzondering. Oplossing — controleer altijd mounted vóór aanroep.

Tweede fout — setState aanroepen binnen build. Dit leidt tot een oneindige lus: build → setState → dirty → build → setState → ... Flutter blokkeert een dergelijke aanroep niet (u krijgt StackOverflowError). setState mag alleen worden aangeroepen als reactie op een gebeurtenis (knopdruk, voltooiing van Future, ontvangst van gegevens uit een stroom).

Derde fout — State-velden wijzigen zonder setState aan te roepen. De ontwikkelaar schrijft _count++ en verwacht dat de UI wordt bijgewerkt. Flutter kan veldwijzigingen niet automatisch volgen — het heeft een expliciet signaal via setState nodig. Dit is een fundamenteel verschil met reactieve frameworks zoals Vue.js, waar gegevenswijziging automatisch een update activeert.

Vierde fout — setState aanroepen met een asynchrone callback (async-lambda). Zoals beschreven in de sectie over asynchroniteit, worden wijzigingen na await niet opgevangen, wat leidt tot moeilijk te reproduceren bugs. Gebruik een synchrone callback en roep setState aan na await.

Notitie over veilige setState

  • Controleer altijd mounted in asynchrone callbacks
  • Roep setState niet aan binnen build
  • Geef geen async-lambda door aan setState
  • Wijzig State-velden niet buiten setState
  • Als u meerdere velden wijzigt — doe dit in één setState

Veelgestelde vragen

Wat doet setState() in Flutter?

setState() meldt Flutter dat de interne gegevens van StatefulWidget zijn gewijzigd en de UI moet worden herbouwd. De methode accepteert een callback, voert deze synchroon uit, markeert de widget als dirty en plant de build-aanroep in het volgende frame.

Wat gebeurt er als ik setState niet aanroep na het wijzigen van een veld?

De UI wordt niet bijgewerkt. Flutter volgt veldwijzigingen niet automatisch. De veldwaarde verandert in het geheugen, maar de widget blijft in de vorige toestand tot de volgende gedwongen herbouw door de ouder.

Kan ik setState aanroepen binnen build?

Nee. Dit leidt tot een oneindige lus: build roept setState aan, die de widget als dirty markeert en opnieuw build aanroept. Flutter blokkeert een dergelijke situatie niet — de applicatie crasht met StackOverflowError.

Hoe vaak wordt build uitgevoerd bij twee opeenvolgende setState?

Build wordt één keer uitgevoerd. Flutter verzamelt alle dirty-elementen en bouwt ze batchgewijs aan het einde van het frame. De tweede setState vóór verwerking van de eerste voegt het element gewoon aan dezelfde lijst van dirty-elementen toe — er vindt geen herhaalde build plaats.

Wat is mounted en waarom is het belangrijk voor setState?

mounted — een booleaanse vlag die aangeeft dat de widget nog in de boom zit. Als u na een asynchrone bewerking setState aanroept zonder mounted te controleren en de widget is al verwijderd — crasht de applicatie met de uitzondering „setState called after dispose”.

Samenvatting

  • setState() — State-methode die Flutter op de hoogte stelt van gegevenswijziging en herbouw van UI in het volgende frame start
  • Werkingsmechanisme — synchrone uitvoering van de callback, markeren van State als dirty, batchgewijze herbouw van alle dirty-elementen aan het einde van het frame
  • Asynchroniteit — async-callbacks in setState werken niet; await moet buiten zijn en setState — na het ontvangen van het resultaat
  • mounted — verplichte controle vóór setState in asynchrone bewerkingen om uitzondering te voorkomen
  • Optimalisatie — minimaliseer het herbouwgebied via const kind-widgets en verplaats animaties naar AnimatedBuilder
  • Alternatieven — gebruik voor globale toestand Riverpod, Bloc of Provider; behoud setState voor lokale gegevens
  • Regel — roep setState niet aan binnen build, geef geen async-lambda door, controleer altijd mounted

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook