State: cos'è, gestione dello stato e principio di funzionamento

Autore: IT Sectr Pubblicato: 2026-07-01 Tempo di lettura: 9 min

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 che memorizza i dati mutabili di StatefulWidget e gestisce la sua ricostruzione tramite setState
  • Ciclo di vita — State passa attraverso initState, didChangeDependencies, build, didUpdateWidget e dispose, ogni fase con uno scopo chiaro
  • mounted — un flag che indica che State è ancora nell'albero dei widget e può chiamare setState in sicurezza
  • widget — un riferimento allo StatefulWidget associato, accessibile tramite la proprietà State per leggere i parametri del genitore
  • Isolamento — State è isolato dagli altri State; per lo scambio di dati si usano InheritedWidget o strumenti esterni di gestione dello stato

Cos'è State in Flutter?

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.

Dove viene memorizzato State?

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.

Ciclo di vita di State

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 — inizializzazione

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

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 — costruzione dell'interfaccia utente

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

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 — rilascio delle risorse

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.

MetodoQuando viene chiamatosuper obbligatorio
initStateAlla creazione di StateSì, nella prima riga
didChangeDependenciesDopo initState e al cambio di InheritedWidget
buildDopo initState, didChangeDependencies, setStateNo
didUpdateWidgetAll'arrivo di un nuovo widget dal genitore
setStateSu chiamata dello sviluppatoreNo
disposeAlla rimozione dall'alberoSì, nell'ultima riga

Come funziona State?

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.

Esempi di codice Dart

Esempio base di State con un campo modificato da un timer. Mostra initState, setState e dispose:

dart
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:

dart
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 vs StatefulWidget

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.

Perché StatefulWidget non può essere 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.

Gestione dello stato tra widget

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.

Stato locale vs globale

  • Locale — nello State di un widget specifico (posizione di scroll, stato di focus)
  • Globale — in un archivio esterno (dati utente, impostazioni, cache)
  • Regola: se i dati sono usati da un solo widget — conservali in State
  • Se i dati sono usati da 2+ widget — spostali in Riverpod/Bloc/Provider

Errori comuni

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.

Verifica di mounted prima di setState

Un pattern di sicurezza per operazioni asincrone in State:

dart
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

In cosa State si differenzia da StatefulWidget?

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.

Quanti oggetti State vengono creati per un StatefulWidget?

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.

Cos'è mounted in State?

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.

State può essere usato senza StatefulWidget?

No. State è sempre legato a uno StatefulWidget specifico tramite generics: State<T extends StatefulWidget>. Creare State direttamente, senza associazione a un widget, è architetturalmente impossibile.

Cosa succede chiamando setState in dispose?

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

  • State — oggetto di gestione dati di StatefulWidget che memorizza campi mutabili e attiva la ricostruzione dell'interfaccia utente tramite setState
  • Ciclo di vita include i metodi obbligatori initState, didChangeDependencies, build, didUpdateWidget e dispose, ciascuno con il proprio scopo
  • mounted — un flag di sicurezza critico che previene chiamate setState dopo la rimozione del widget dall'albero
  • widget — proprietà di State per accedere ai parametri dello StatefulWidget associato, aggiornata tramite didUpdateWidget
  • Isolamento — State non ha accesso ad altri State; la comunicazione tra widget viene implementata tramite InheritedWidget o strumenti esterni
  • setState — non chiama build immediatamente, ma segna solo State come sporco per la ricostruzione nel fotogramma successivo
  • Regola — usa State per dati locali del widget; sposta lo stato globale in livelli esterni (Riverpod, Bloc)

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.

Discuti il progetto

Leggi anche