State — obiectul central de gestionare a datelor în Flutter, asociat cu StatefulWidget și responsabil pentru stocarea informațiilor modificabile și construirea interfeței. Conform documentației oficiale Flutter (Flutter.dev, 2026), State există pe tot parcursul ciclului de viață al widgetului și supraviețuiește reconstruirilor acestuia, asigurând consistența datelor între actualizările UI. Spre deosebire de widgetul însuși, State își poate modifica câmpurile și poate iniția reconstruirea prin apelarea setState.
Principalele
State — este un obiect în arhitectura Flutter care stochează datele modificabile ale StatefulWidget și determină modul în care aceste date sunt afișate în interfață. Fiecare StatefulWidget la încorporarea în arbore creează exact un obiect State prin metoda createState. State există independent de widget: dacă părintele reconstruiește StatefulWidget cu parametri noi, State rămâne același și primește widgetul actualizat prin proprietatea widget.
Conform Flutter Architectural Overview (Google, 2026), separarea Widget și State este o decizie arhitecturală conștientă, care permite frameworkului să reutilizeze elementele arborelui. Widgetul (descriere ușoară) poate fi creat și distrus de multiple ori, dar State (obiect greu cu date) rămâne în memorie cât timp elementul se află în arbore. Aceasta previne pierderea datelor la reconstruiri frecvente ale widgeturilor părinte.
State implementează interfața StatefulWidget prin generic: class _MyState extends State<MyWidget>. Genericul leagă State de un tip specific de StatefulWidget, asigurând acces tip-sigur la câmpurile sale prin proprietatea widget.
Obiectul State este stocat în StatefulElement — stratul intermediar între Widget și RenderObject. StatefulElement creează State prin createState, păstrează referința la acesta și transmite State ca proprietar. Elementul este distrus doar când widgetul este eliminat din arbore — până atunci State trăiește în memorie.
Ciclul de viață al State este determinist și constă dintr-o succesiune strictă de apeluri. Înțelegerea acestei succesiuni este baza lucrului corect cu resursele și prevenirii scurgerilor de memorie.
initState este apelat primul la crearea State. În această metodă se inițializează controllere, abonamente la fluxuri de date, temporizatoare și valorile inițiale ale câmpurilor. Apelarea super.initState() în prima linie este obligatorie. În etapa initState, arborele de widgeturi nu este încă complet montat, deci metode precum MediaQuery.of(context) pot funcționa incorect.
didChangeDependencies este apelat după initState și la fiecare modificare a dependențelor InheritedWidget. Exact aici, nu în initState, trebuie apelat MediaQuery.of(context) sau Theme.of(context), deoarece în acest moment arborele este deja montat. Această metodă este de asemenea apelată dacă widgetul este mutat într-un alt context unde InheritedWidget oferă valori diferite.
build — metoda principală a State, care returnează arborele de widgeturi. Apelată după initState, după didChangeDependencies și după fiecare setState. Metoda build nu trebuie să aibă efecte secundare — ea doar descrie interfața pe baza valorilor curente ale câmpurilor State.
didUpdateWidget este apelat când părintele reconstruiește StatefulWidget cu parametri noi. State obține acces la widgetul vechi prin oldWidget și poate să îl compare cu cel nou. Dacă parametrii s-au schimbat, se poate actualiza starea, încărca date noi sau reporni animația.
dispose — metoda finală în care sunt eliberate toate resursele: controllere, abonamente, temporizatoare. După dispose, State este marcat ca mort: mounted returnează false, apelarea setState aruncă o excepție. Apelarea super.dispose() în ultima linie a metodei este obligatorie.
| Metodă | Când este apelată | super obligatoriu |
|---|---|---|
| initState | La crearea State | Da, în prima linie |
| didChangeDependencies | După initState și la schimbarea InheritedWidget | Da |
| build | După initState, didChangeDependencies, setState | Nu |
| didUpdateWidget | La un widget nou de la părinte | Da |
| setState | La apelul dezvoltatorului | Nu |
| dispose | La eliminarea din arbore | Da, în ultima linie |
Mecanismul de funcționare al State se bazează pe trei principii cheie: asocierea cu Element, reactivitatea prin setState și accesul la părinte prin proprietatea widget. Când Flutter construiește arborele de elemente și întâlnește StatefulElement, apelează createState al widgetului asociat. State-ul creat este stocat în element și există până când elementul este eliminat.
La apelarea setState, State se marchează ca „murdar„ (dirty) și planifică reconstruirea pentru următorul cadru. Important: setState nu apelează build imediat — doar înregistrează necesitatea reconstruirii. Flutter colectează toate elementele murdare din cadrul curent și le reconstruiește în lot, ceea ce optimizează performanța. După apelarea build, State revine la starea „curat„ (clean).
Proprietatea widget permite State să citească parametrii transmiși constructorului StatefulWidget. Deoarece StatefulWidget este imuabil (ca StatelessWidget), câmpurile sale nu se schimbă — la modificarea parametrilor, părintele creează un widget nou, iar State îl primește prin didUpdateWidget. Aceasta garantează că State lucrează întotdeauna cu datele actualizate ale părintelui.
Exemplul de bază State cu un câmp modificat de un temporizator. Demonstrează initState, setState și dispose:
class _TimerWidgetState extends State<TimerWidget> {
int _seconds = 0;
Timer? _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(
const Duration(seconds: 1),
(_) => setState(() => _seconds++),
);
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('$_seconds secunde au trecut');
}
}
Exemplu cu utilizarea proprietății widget pentru accesul la parametrii părintelui și reacția la modificările lor prin didUpdateWidget:
class _GreetingState extends State<GreetingWidget> {
String _displayName = '';
@override
void initState() {
super.initState();
_displayName = _formatName(widget.name);
}
@override
void didUpdateWidget(GreetingWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.name != oldWidget.name) {
setState(() {
_displayName = _formatName(widget.name);
});
}
}
String _formatName(String name) => name.trim().isEmpty ? 'Guest' : name;
@override
Widget build(BuildContext context) {
return Text('Salut, $_displayName!');
}
}
În al doilea exemplu, State urmărește modificarea parametrului de intrare name și reformatează afișarea doar la schimbarea reală. Fără verificarea widget.name != oldWidget.name, metoda ar fi apelată la fiecare reconstruire a părintelui, chiar dacă numele nu s-a schimbat — o muncă inutilă pentru framework.
State și StatefulWidget sunt două clase diferite în arhitectura Flutter, cu roluri diferite. StatefulWidget — este o învelitoare ușoară imuabilă care descrie configurația widgetului și creează State. State — este un obiect greu care stochează date modificabile, gestionează abonamentele și construiește UI. Această separare permite Flutter să distrugă și să creeze widgeturi fără a pierde starea.
Toate câmpurile StatefulWidget trebuie să fie final și setate în constructor — ele nu se schimbă după creare. State, dimpotrivă, poate modifica oricând câmpurile sale, dar toate modificările trebuie precedate de apelarea setState, pentru ca Flutter să afle despre necesitatea reconstruirii. Aceasta este diferența cheie: StatefulWidget este „ce să arăți„, State — „cum să arăți și ce date să folosești„.
Conform Flutter source code analysis (Flutter SDK, 2026), StatefulWidget conține un singur câmp obligatoriu — createState, în timp ce State are acces la BuildContext, poate să se aboneze la fluxuri, să gestioneze animații și controllere. Se recomandă menținerea StatefulWidget cât mai simplu, transferând toată logica în State.
Separarea Widget și State — o decizie arhitecturală care asigură imutabilitatea configurației. Dacă StatefulWidget ar stoca el însuși starea, la fiecare reconstruire a părintelui starea s-ar pierde. Extrăgând starea într-un obiect separat, Flutter garantează că datele supraviețuiesc reconstruirilor, iar widgeturile rămân ușoare și comparabile.
Obiectul State este izolat — nu are acces direct la State-ul altor widgeturi. Pentru schimbul de date între widgeturi se folosesc InheritedWidget sau instrumente externe de gestionare a stării: Provider, Riverpod, Bloc, Redux. Fiecare abordare rezolvă problema în felul său: InheritedWidget funcționează prin arborele de widgeturi, Provider — prin containerul DI, Bloc — prin fluxuri de evenimente.
Alegerea instrumentului depinde de amploarea proiectului. Pentru o aplicație mică, sunt suficiente InheritedWidget și State local. Pentru un proiect mediu și mare, se recomandă Riverpod sau Bloc — ele asigură testabilitate, predictibilitate și separarea logicii de UI. State în acest caz este folosit doar pentru datele locale ale widgetului (focus, scroll, animație).
Conform Flutter Community Survey 2025 (Flutter Foundation, decembrie 2025), Riverpod este cea mai populară soluție pentru gestionarea stării în proiecte noi (38%), urmată de Bloc (31%) și Provider (22%). Toate trei instrumentele sunt compatibile cu State și nu necesită renunțarea la ciclul de viață standard.
Prima greșeală — a uita să verificați mounted înainte de setState într-un callback asincron. Când widgetul a fost eliminat din arbore (de exemplu, utilizatorul a părăsit ecranul), dar operația asincronă (cererea HTTP) încă se execută, după finalizarea ei State este deja mort. Apelarea setState într-un State mort aruncă o excepție. Verificarea if (mounted) setState(...) rezolvă problema.
A doua greșeală — inițializarea dependențelor InheritedWidget în initState în loc de didChangeDependencies. În initState contextul nu este încă montat, deci MediaQuery.of(context) va arunca o excepție. Toate dependențele de InheritedWidget trebuie configurate în didChangeDependencies sau în build.
A treia greșeală — mutarea câmpurilor fără apelarea setState. Dacă dezvoltatorul modifică un câmp al State fără setState, Flutter nu va afla despre modificare și UI nu se va actualiza. De exemplu: _list.add(item) fără setState((){}) ulterior va modifica lista, dar ecranul va rămâne același.
Modelul de siguranță pentru operații asincrone în State:
Future<void> _fetchData() async {
final data = await ApiService.fetch();
if (mounted) {
setState(() => _data = data);
}
}
Verificarea mounted garantează că setState este apelat doar pentru un State viu, prevenind excepția „setState called after dispose„.
Întrebări frecvente
StatefulWidget — configurația imuabilă a widgetului, iar State — un obiect modificabil care stochează date și gestionează ciclul de viață. Widgetul poate fi recreat, State — nu. StatefulWidget creează State prin createState.
Exact unul. Metoda createState este apelată o singură dată la prima încorporare a StatefulWidget în arbore. Chiar dacă părintele se reconstruiește de multiple ori, obiectul State rămâne același, până când se schimbă tipul sau Key-ul widgetului.
mounted — un flag boolean care arată dacă State se află în arborele de widgeturi. După apelarea dispose, mounted devine false. Folosit pentru verificare înainte de setState în callback-uri asincrone, pentru a evita excepția.
Nu. State este întotdeauna legat de un StatefulWidget specific prin generic: State<T extends StatefulWidget>. Crearea State direct, fără asociere cu un widget, este imposibilă arhitectural.
Se aruncă o excepție: „setState called after dispose„. După apelarea dispose, State este considerat mort și orice încercare de a reconstrui UI prin setState este interzisă. Soluția — verificați mounted înainte de fiecare setState.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și