State è l'oggetto centrale di gestione dei dati in Flutter, associato a StatefulWidget e responsabile della memorizzazione di informazioni mutabili e della creazione dell'interfaccia. Secondo la documentazione ufficiale di Flutter (Flutter.dev, 2026), State esiste per tutto il ciclo di vita del widget e sopravvive alle sue ricostruzioni, garantendo la coerenza dei dati tra gli aggiornamenti dell'interfaccia utente. A differenza del widget stesso, State può modificare i suoi campi e avviare la ricostruzione tramite la chiamata setState.
Punti chiave
State è un oggetto nell'architettura Flutter che memorizza i dati mutabili di un StatefulWidget e determina come questi dati vengono visualizzati nell'interfaccia. Ogni StatefulWidget, quando viene inserito nell'albero, crea esattamente un oggetto State tramite il metodo createState. State esiste indipendentemente dal widget: se il genitore ricostruisce lo StatefulWidget con nuovi parametri, State rimane lo stesso e riceve il widget aggiornato tramite la proprietà widget.
Secondo la Panoramica Architetturale di Flutter (Google, 2026), la separazione di Widget e State è una decisione architetturale deliberata che consente al framework di riutilizzare gli elementi dell'albero. Il widget (una descrizione leggera) può essere creato e distrutto più volte, ma State (un oggetto pesante con dati) rimane in memoria finché l'elemento è nell'albero. Ciò previene la perdita di dati durante le frequenti ricostruzioni dei widget genitori.
State implementa l'interfaccia StatefulWidget tramite generics: class _MyState extends State<MyWidget>. Il generic lega State a un tipo specifico di StatefulWidget, fornendo un accesso type-safe ai suoi campi tramite la proprietà widget.
L'oggetto State viene memorizzato in StatefulElement — un livello intermedio tra Widget e RenderObject. StatefulElement crea State tramite createState, mantiene un riferimento ad esso e passa State come proprietario. L'Element viene distrutto solo quando il widget viene rimosso dall'albero — fino ad allora, State vive in memoria.
Il ciclo di vita di State è deterministico e consiste in una sequenza rigorosa di chiamate. Comprendere questa sequenza è la base per una corretta gestione delle risorse e la prevenzione di perdite di memoria.
initState viene chiamato per primo quando State viene creato. In questo metodo vengono inizializzati controller, abbonamenti a stream, timer e valori iniziali dei campi. È obbligatorio chiamare super.initState() nella prima riga. Nella fase initState, l'albero dei widget non è ancora completamente montato, quindi metodi come MediaQuery.of(context) potrebbero non funzionare correttamente.
didChangeDependencies viene chiamato dopo initState e ogni volta che le dipendenze InheritedWidget cambiano. È qui, non in initState, che si dovrebbe chiamare MediaQuery.of(context) o Theme.of(context), poiché a questo punto l'albero è già montato. Questo metodo viene chiamato anche se il widget si sposta in un contesto diverso dove InheritedWidget fornisce altri valori.
build è il metodo principale di State che restituisce un albero di widget. Viene chiamato dopo initState, dopo didChangeDependencies e dopo ogni setState. Il metodo build non dovrebbe avere effetti collaterali — descrive solo l'interfaccia basata sui valori correnti dei campi di State.
didUpdateWidget viene chiamato quando il genitore ricostruisce lo StatefulWidget con nuovi parametri. State ottiene accesso al vecchio widget tramite oldWidget e può confrontarlo con il nuovo. Se i parametri sono cambiati, è possibile aggiornare lo stato, caricare nuovi dati o riavviare un'animazione.
dispose è il metodo finale in cui tutte le risorse vengono rilasciate: controller, abbonamenti, timer. Dopo dispose, State viene marcato come morto: mounted restituisce false, chiamare setState lancia un'eccezione. È obbligatorio chiamare super.dispose() nell'ultima riga del metodo.
| Metodo | Quando viene chiamato | super obbligatorio |
|---|---|---|
| initState | Alla creazione di State | Sì, nella prima riga |
| didChangeDependencies | Dopo initState e al cambio di InheritedWidget | Sì |
| build | Dopo initState, didChangeDependencies, setState | No |
| didUpdateWidget | All'arrivo di un nuovo widget dal genitore | Sì |
| setState | Su chiamata dello sviluppatore | No |
| dispose | Alla rimozione dall'albero | Sì, nell'ultima riga |
Il meccanismo di funzionamento di State si basa su tre principi chiave: associazione con Element, reattività tramite setState e accesso al genitore attraverso la proprietà widget. Quando Flutter costruisce l'albero degli elementi e incontra un StatefulElement, chiama createState del widget associato. Lo State creato viene memorizzato nell'elemento e esiste fino a quando l'elemento non viene rimosso.
Quando viene chiamato setState, State si segna come sporco e pianifica una ricostruzione per il fotogramma successivo. Importante: setState non chiama build immediatamente — registra solo la necessità di ricostruzione. Flutter raccoglie tutti gli elementi sporchi del fotogramma corrente e li ricostruisce in batch, ottimizzando le prestazioni. Dopo la chiamata a build, State torna allo stato pulito.
La proprietà widget permette a State di leggere i parametri passati al costruttore di StatefulWidget. Poiché StatefulWidget è immutabile (come StatelessWidget), i suoi campi non cambiano — quando i parametri cambiano, il genitore crea un nuovo widget e State lo riceve tramite didUpdateWidget. Ciò garantisce che State lavori sempre con i dati correnti del genitore.
Esempio base di State con un campo modificato da un timer. Mostra initState, setState e 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 seconds elapsed');
}
}
Esempio che usa la proprietà widget per accedere ai parametri del genitore e reagire ai loro cambiamenti tramite 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('Hello, $_displayName!');
}
}
Nel secondo esempio, State traccia i cambiamenti del parametro di input name e riformatta la visualizzazione solo quando si verifica un cambiamento reale. Senza il controllo widget.name != oldWidget.name, il metodo verrebbe chiamato a ogni ricostruzione del genitore, anche se il nome non fosse cambiato — lavoro inutile per il framework.
State e StatefulWidget sono due classi diverse nell'architettura Flutter che svolgono ruoli differenti. StatefulWidget è un involucro immutabile leggero che descrive la configurazione del widget e crea State. State è un oggetto pesante che memorizza dati mutabili, gestisce abbonamenti e costruisce l'interfaccia utente. Questa separazione permette a Flutter di distruggere e creare widget senza perdere lo stato.
Tutti i campi di StatefulWidget devono essere final e impostati nel costruttore — non cambiano dopo la creazione. State, d'altra parte, può modificare i suoi campi in qualsiasi momento, ma tutte le modifiche devono essere precedute da una chiamata setState affinché Flutter venga a conoscenza della necessità di ricostruzione. Questa è la differenza chiave: StatefulWidget è “cosa mostrare”, State è “come mostrare e quali dati usare”.
Secondo l'analisi del codice sorgente di Flutter (Flutter SDK, 2026), StatefulWidget contiene un solo campo obbligatorio — createState, mentre State ha accesso a BuildContext, può abbonarsi a stream, gestire animazioni e controller. Si raccomanda di mantenere StatefulWidget il più semplice possibile, spostando tutta la logica in State.
La separazione di Widget e State è una decisione architetturale che garantisce l'immutabilità della configurazione. Se StatefulWidget memorizzasse lo stato stesso, lo stato andrebbe perso a ogni ricostruzione del genitore. Spostando lo stato in un oggetto separato, Flutter garantisce che i dati sopravvivano alle ricostruzioni, mentre i widget rimangono leggeri e confrontabili.
L'oggetto State è isolato — non ha accesso diretto allo State di altri widget. Per lo scambio di dati tra widget si usano InheritedWidget o strumenti esterni di gestione dello stato: Provider, Riverpod, Bloc, Redux. Ogni approccio risolve il problema in modo diverso: InheritedWidget funziona attraverso l'albero dei widget, Provider attraverso un contenitore DI, Bloc attraverso flussi di eventi.
La scelta dello strumento dipende dalla scala del progetto. Per un'applicazione piccola, InheritedWidget e State locale sono sufficienti. Per progetti medi e grandi, si raccomanda Riverpod o Bloc — garantiscono testabilità, prevedibilità e separazione della logica dall'interfaccia utente. State viene quindi utilizzato solo per dati locali del widget (focus, scroll, animazione).
Secondo il Sondaggio della Comunità Flutter 2025 (Flutter Foundation, dicembre 2025), Riverpod è la soluzione di gestione dello stato più popolare nei nuovi progetti (38%), seguito da Bloc (31%) e Provider (22%). Tutti e tre gli strumenti sono compatibili con State e non richiedono di abbandonare il ciclo di vita standard.
Il primo errore è dimenticare di verificare mounted prima di setState in un callback asincrono. Quando un widget viene rimosso dall'albero (ad esempio, l'utente ha lasciato la schermata), ma un'operazione asincrona (richiesta HTTP) è ancora in esecuzione, dopo il suo completamento State è già morto. Chiamare setState in uno State morto lancia un'eccezione. Il controllo if (mounted) setState(...) risolve il problema.
Il secondo errore è inizializzare le dipendenze InheritedWidget in initState invece che in didChangeDependencies. In initState, il contesto non è ancora montato, quindi MediaQuery.of(context) lancerà un'eccezione. Tutte le dipendenze InheritedWidget dovrebbero essere configurate in didChangeDependencies o in build.
Il terzo errore è mutare i campi senza chiamare setState. Se uno sviluppatore modifica un campo State senza setState, Flutter non verrà a conoscenza del cambiamento e l'interfaccia utente non verrà aggiornata. Ad esempio: _list.add(item) senza un successivo setState((){}) modificherà la lista, ma lo schermo rimarrà invariato.
Un pattern di sicurezza per operazioni asincrone in State:
Future<void> _fetchData() async {
final data = await ApiService.fetch();
if (mounted) {
setState(() => _data = data);
}
}
Verificare mounted garantisce che setState venga chiamato solo su uno State vivo, prevenendo l'eccezione “setState called after dispose”.
Domande frequenti
StatefulWidget è una configurazione immutabile del widget, mentre State è un oggetto mutabile che memorizza dati e gestisce il ciclo di vita. Il widget può essere ricreato, State no. StatefulWidget crea State tramite createState.
Esattamente uno. Il metodo createState viene chiamato una volta quando lo StatefulWidget viene inserito per la prima volta nell'albero. Anche se il genitore si ricostruisce più volte, l'oggetto State rimane lo stesso fino a quando il tipo o la Key del widget non cambiano.
mounted è un flag booleano che mostra se State è nell'albero dei widget. Dopo la chiamata a dispose, mounted diventa false. Viene usato per la verifica prima di setState in callback asincroni per evitare eccezioni.
No. State è sempre legato a uno StatefulWidget specifico tramite generics: State<T extends StatefulWidget>. Creare State direttamente, senza associazione a un widget, è architetturalmente impossibile.
Viene lanciata un'eccezione: “setState called after dispose”. Dopo dispose, State è considerato morto e qualsiasi tentativo di ricostruire l'interfaccia utente tramite setState è vietato. La soluzione è verificare mounted prima di ogni setState.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche