setState() — den viktigaste metoden i State i Flutter, som meddelar ramverket om dataförändring och startar ombyggnad av gränssnittet. Enligt officiell Flutter-dokumentation (Flutter.dev, 2026) är setState den huvudsakliga reaktivitetsmekanismen i StatefulWidget: utan anrop kommer UI inte att få veta om ändringar i State-fälten och förbli i tidigare tillstånd. Metoden accepterar en VoidCallback, inom vilken utvecklaren modifierar de föränderliga fälten, varefter Flutter automatiskt anropar build för att bygga om widgeten.
Huvudpunkter
setState() — en inbyggd metod i klassen State i Flutter, avsedd att meddela ramverket att widgetens interna tillstånd har ändrats och UI måste byggas om. Utan setState-anrop vet Flutter inte om förändringar — även om State-fält har modifierats, förblir gränssnittet oförändrat tills nästa tvingade ombyggnad av föräldern.
Metodens signatur: void setState(VoidCallback fn). Callbacken exekveras synkront inom setState och först efter dess slutförande markeras State som dirty. Detta garanterar att alla ändringar tillämpas atomärt före ombyggnad. Enligt Dart Language Specification (Dart Team, 2026) förhindrar setState:s atomicitet race-förhållanden, där build skulle kunna se ett delvis uppdaterat tillstånd.
setState accepterar inga argument, returnerar inget värde och kan inte åsidosättas. Det är en final (sealed) metod i klassen State. Utvecklaren kan inte ändra dess beteende — kan endast använda den enligt dess syfte. Försök att anropa setState utanför State (t.ex. från en annan klass) är omöjligt, eftersom metoden deklareras i klassen State.
En vanlig missuppfattning — att tro att setState själv ändrar tillståndet. Detta är inte sant. setState anropar bara den skickade callbacken (där utvecklaren ändrar fälten) och signalerar sedan ramverket om behovet av build. Callbacken är obligatorisk — att skicka null eller en tom callback kommer att orsaka ett fel.
Funktionsmekanismen för setState() kan delas in i fyra steg. Första — anrop av metoden med callback. Andra — synkron exekvering av callbacken, inom vilken State-fälten ändras. Tredje — State markeras som dirty i det speciella fältet _dirty. Fjärde — i slutet av den aktuella microtasken genomgår Flutter alla dirty-element och anropar deras build i ordning efter förekomst i trädet.
En viktig detalj: setState anropar inte build omedelbart. Flutter använder en batchuppdateringsstrategi: alla dirty-element samlas in och byggs om i en bildruta. Detta innebär att om setState anropas flera gånger inom samma synkrona block, kommer build endast att exekveras en gång — efter att alla ändringar slutförts. Denna optimering förhindrar flera ombyggnader per bildruta.
Enligt Flutter Engine Team (Google, 2025) baseras mekanismen med dirty-flaggor på genomgång av BuildOwner._dirtyElements. Varje dirty StatefulElement läggs till i listan och bearbetas i bildrutans uppdateringsfas. Om widgeten har tagits bort från trädet före bearbetning, utesluts den automatiskt från listan över dirty-element.
Grundläggande exempel på setState() med räknare-ökning. Demonstrerar korrekt användning: ändra fält inom callbacken:
class _CounterState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++; // mutera fält inom callback
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('$_count'),
);
}
}
Exempel med textfält och controller — setState() för att hantera lösenordssynlighet:
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();
}
}
I detta exempel ändrar setState() endast det booleska fältet _obscured, vilket orsakar ombyggnad av TextField med ny ikon och visningsläge. Textcontrollern återskapas inte — den initieras en gång i initState och frigörs i dispose.
Om flera fält behöver ändras, utförs alla ändringar inom en setState. Detta garanterar att build ser ett konsekvent tillstånd:
setState(() {
_isLoading = false;
_items = newItems;
_error = null;
});
Tre fält ändras i en callback — build exekveras en gång och ser alla ändringar samtidigt. Om varje anrop var en separat setState, skulle build ändå exekveras en gång tack vare batchbearbetning av dirty-element.
En av de viktigaste nyanserna hos setState() — dess beteende med asynkrona operationer. setState-callbacken exekveras synkront, men om await anropas inom den, kommer koden efter await att exekveras efter att setState har slutfört sitt arbete. Detta innebär att ändringar av fält efter await inte kommer att fångas av den aktuella setState.
Korrekt tillvägagångssätt: den asynkrona operationen utförs utanför setState, och setState anropas efter dess slutförande. All kod mellan att ta emot resultatet och anropa setState exekveras i synkront sammanhang efter await:
// KORREKT: await utanför setState
Future<void> _loadData() async {
final result = await ApiService.fetchData();
setState(() {
_data = result;
_isLoading = false;
});
}
// FEL: await inom setState — ingen uppdateringsgaranti
void _loadDataWrong() {
setState(() async {
_data = await ApiService.fetchData(); // setState återvänder innan await slutförs
_isLoading = false; // denna kod fångas inte av setState
});
}
Enligt Flutter-dokumentation (Dart async patterns, 2026) är att skicka en async-callback till setState ett antimönster, eftersom setState förväntar sig VoidCallback (en synkron funktion), medan en async-funktion returnerar en Future som ignoreras. Ändringar efter första await i en sådan callback kommer inte att bearbetas korrekt av ramverket.
Innan du anropar setState() efter en asynkron operation, kontrollera alltid mounted:
if (mounted) {
setState(() => _data = data);
}
Om widgeten har tagits bort från trädet under exekveringen av den asynkrona operationen, kommer mounted att bli false och setState kommer inte att anropas. Detta förhindrar undantag och resursläckor.
setState() — en bekväm men potentiellt dyr mekanism, om den används tanklöst. Varje setState-anrop bygger om hela widgeten och alla dess avkomlingar (om de inte är const). I djupa träd eller vid frekventa anrop kan detta leda till FPS-fall.
Huvudsakliga optimeringsstrategier: minimera ombyggnadsområdet (flytta variabla UI-delar till separata StatefulWidgets), använd const för oföränderliga avkomlingar och undvik setState-anrop i förälder-widgetar om endast en liten UX-detalj har ändrats. Om tillståndet uppdateras med hög frekvens (animering, dataström), överväg AnimatedBuilder eller ValueListenableBuilder.
Enligt Flutter Performance Best Practices (Flutter.dev, februari 2026) visar profilering av verkliga applikationer att upp till 40% av alla setState-anrop kan ersättas med const-underordnade widgetar eller reaktiva builders (StreamBuilder, FutureBuilder). Detta minskar den genomsnittliga bildrutans byggtid med 15–25%.
| Scenario | Alternativ | Fördel |
|---|---|---|
| Animering | AnimatedBuilder | Bygger endast om den animerade widgeten |
| Dataström | StreamBuilder | Reagerar på varje element i strömmen |
| Framtida resultat | FutureBuilder | Hanterar laddnings-/feltillstånd |
| Lokalt värde | ValueListenableBuilder | Reagerar på ändring av ett enda värde |
Trots mångsidigheten hos setState() används den i stora projekt främst för lokalt tillstånd. För globalt eller delat tillstånd används specialiserade lösningar, som var och en ersätter eller omsluter setState.
Provider använder ChangeNotifier + notifyListeners som en analog till setState, men med möjlighet för flera widgetar att prenumerera. Bloc använder Streams — tillståndet ändras genom att lägga till händelser i StreamController. Riverpod kombinerar tillvägagångssätt och erbjuder både lokal (StateProvider) och asynkron (AsyncNotifier) hantering utan bindning till StatefulWidget. Alla tre tillvägagångssätt eliminerar behovet av att manuellt anropa setState — UI-uppdatering sker automatiskt vid dataändring.
Enligt Flutter Community Survey 2025 (Flutter Foundation, december 2025) använder 74% av utvecklarna minst ett tillståndshanteringsverktyg utöver setState. Samtidigt fortsätter 92% att använda setState för lokala data i textfält, kryssruta eller enkel räknare — detta anses vara bästa praxis (best practice).
Det första och farligaste misstaget — anropa setState efter dispose. En asynkron operation startade i initState, användaren lämnade skärmen, widgeten togs bort och callbacken för den asynkrona operationen anropar setState — applikationen kraschar med ett undantag. Lösning — kontrollera alltid mounted före anrop.
Andra misstaget — anropa setState inom build. Detta leder till en oändlig loop: build → setState → dirty → build → setState → ... Flutter blockerar inte ett sådant anrop (du får StackOverflowError). setState kan endast anropas som svar på en händelse (knapptryckning, slutförande av Future, mottagning av data från en ström).
Tredje misstaget — ändra State-fält utan att anropa setState. Utvecklaren skriver _count++ och förväntar sig att UI uppdateras. Flutter kan inte automatiskt spåra fältändringar — det behöver en explicit signal via setState. Detta är en grundläggande skillnad från reaktiva ramverk som Vue.js, där dataändring automatiskt utlöser uppdatering.
Fjärde misstaget — anropa setState med en asynkron callback (async-lambda). Som beskrivits i avsnittet om asynkronitet kommer ändringar efter await inte att fångas, vilket leder till svårreproducerbara buggar. Använd en synkron callback och anropa setState efter await.
mounted i asynkrona callbacksVanliga frågor
setState() meddelar Flutter att den interna datan i StatefulWidget har ändrats och UI måste byggas om. Metoden accepterar en callback, exekverar den synkront, markerar widgeten som dirty och planerar build-anrop i nästa bildruta.
UI uppdateras inte. Flutter spårar inte fältändringar automatiskt. Fältets värde ändras i minnet, men widgeten förblir i tidigare tillstånd tills nästa tvingade ombyggnad av föräldern.
Nej. Detta leder till en oändlig loop: build anropar setState, som markerar widgeten som dirty och anropar build igen. Flutter blockerar inte en sådan situation — applikationen kraschar med StackOverflowError.
Build exekveras en gång. Flutter samlar alla dirty-element och bygger om dem i batch i slutet av bildrutan. Den andra setState före bearbetning av den första lägger helt enkelt till elementet i samma lista av dirty-element — ingen upprepad build sker.
mounted — en boolesk flagga som visar att widgeten fortfarande finns i trädet. Om du efter en asynkron operation anropar setState utan att kontrollera mounted, och widgeten redan har tagits bort — kraschar applikationen med undantaget „setState called after dispose”.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också