StatefulWidget: cos'è, ciclo di vita e principio di funzionamento

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

StatefulWidget è un widget Flutter con stato mutabile, che consente all'interfaccia utente di reagire alle azioni dell'utente, agli eventi asincroni e ai flussi di dati. Secondo la documentazione ufficiale di Flutter (Flutter.dev, 2026), StatefulWidget viene utilizzato per tutti gli elementi interattivi dell'applicazione: moduli di input, animazioni, caselle di controllo, interruttori e schermate che caricano dati dalla rete. A differenza di StatelessWidget, crea un oggetto State separato che persiste per tutto il suo ciclo di vita e può essere ricostruito senza ricreare il widget stesso.

Punti chiave

  • StatefulWidget è un widget che può modificare il suo stato durante l'esecuzione, attivando la ricostruzione dell'interfaccia tramite setState
  • Ciclo di vita — StatefulWidget attraversa le fasi createState, initState, didChangeDependencies, build, didUpdateWidget, dispose
  • Oggetto State è un oggetto separato che memorizza lo stato ed esiste indipendentemente dal widget per tutta la sua durata
  • setState è l'unico modo legittimo per notificare a Flutter la necessità di ricostruire il widget dopo una modifica dei dati
  • Prestazioni — l'uso eccessivo di StatefulWidget aumenta il consumo di memoria e il tempo di rendering

Cos'è StatefulWidget?

StatefulWidget è una classe Flutter che può modificare il suo stato in risposta alle azioni dell'utente, agli eventi di sistema o alle operazioni asincrone. A differenza di StatelessWidget, StatefulWidget non viene renderizzato direttamente — crea un oggetto State che si occupa del rendering. Questa separazione in due classi (Widget e State) consente a Flutter di ricostruire l'interfaccia senza ricreare il widget stesso, offrendo un vantaggio significativo in termini di prestazioni durante gli aggiornamenti frequenti.

L'architettura di StatefulWidget segue il pattern di “separazione tra mutabile e immutabile”: il widget stesso rimane immutabile (come StatelessWidget), mentre tutto lo stato mutabile viene memorizzato in un oggetto State separato. Ciò consente a Flutter di riutilizzare i widget confrontandoli per tipo e Key, preservando allo stesso tempo lo stato effettivo tra le ricostruzioni.

Secondo Google (Flutter Architectural Overview, 2026), StatefulWidget è ottimale per scenari in cui lo stato cambia più di una volta durante la vita del widget: campi di testo, animazioni, timer, flussi di dati, caricamenti asincroni. Per l'inizializzazione una tantum, StatelessWidget è sufficiente.

Quando è necessario StatefulWidget

StatefulWidget è obbligatorio quando il widget deve rispondere a eventi esterni: clic su pulsanti, completamento di richieste HTTP, aggiornamenti di dati del database, abbonamenti WebSocket. È necessario anche per widget con animazioni, campi di testo con controller e componenti che gestiscono il focus. Se un widget visualizza solo dati e non genera eventi, usa StatelessWidget.

Struttura interna

StatefulWidget è composto da due classi: il StatefulWidget stesso (leggero, immutabile) e State (pesante, mutabile). Il framework crea State tramite il metodo createState(), chiamato una volta quando viene inserito nell'albero. State riceve un riferimento al widget tramite la proprietà widget e può accedere ai suoi campi in qualsiasi momento del ciclo di vita.

Ciclo di vita di StatefulWidget

Il ciclo di vita di StatefulWidget comprende sei fasi principali, ognuna delle quali fornisce un metodo sovrascrivibile per eseguire compiti specifici. Comprendere queste fasi è fondamentale per una corretta gestione delle risorse e per evitare perdite di memoria.

createState

createState è il primo metodo del ciclo di vita, chiamato quando StatefulWidget viene inserito nell'albero. Deve restituire una nuova istanza di State associata a questo widget. Questo metodo viene chiamato esattamente una volta durante l'intera vita dell'elemento. È importante non eseguire operazioni pesanti qui — createState dovrebbe essere il più leggero possibile.

initState

initState viene chiamato immediatamente dopo la creazione di State, prima della prima costruzione dell'interfaccia. Qui si esegue: inizializzazione dei controller (TextEditingController, AnimationController), abbonamento ai flussi di dati (StreamSubscription), configurazione dei timer e inizializzazione dei campi. Secondo la documentazione di Flutter (Flutter.dev, 2026), non è possibile chiamare BuildContext.of() in initState — l'albero non è ancora completamente montato.

didChangeDependencies

didChangeDependencies viene chiamato dopo initState e ogni volta che le dipendenze di InheritedWidget cambiano. Questo è un luogo adatto per chiamare MediaQuery.of(context) o abbonarsi a Theme — valori che possono cambiare durante l'esecuzione dell'applicazione. Se un widget usa InheritedWidget, la logica di inizializzazione dovrebbe essere qui, non in initState.

build e didUpdateWidget

build è il metodo principale che restituisce l'albero dei widget. Viene chiamato dopo initState, dopo didChangeDependencies e dopo ogni setState. didUpdateWidget viene chiamato quando il genitore si ricostruisce e passa un StatefulWidget con nuovi parametri. Qui è possibile confrontare i campi vecchi e nuovi del widget e, se necessario, aggiornare lo stato.

dispose

dispose è la fase finale del ciclo di vita. Qui tutte le risorse vengono liberate: gli abbonamenti ai flussi vengono cancellati, i controller vengono rimossi, i timer vengono annullati. Non chiamare dispose porta a perdite di memoria. Dopo dispose, State è considerato morto — chiamare setState al suo interno genera un'eccezione.

Come funziona StatefulWidget?

Il meccanismo di funzionamento di StatefulWidget si basa sul lavoro coordinato di tre entità: Widget (descrizione leggera), Element (livello intermedio) e State (archiviazione dati). Quando Flutter incontra un StatefulWidget nella descrizione, crea un StatefulElement, che chiama createState e memorizza un riferimento all'oggetto State. Quando il genitore si ricostruisce, Flutter confronta il nuovo widget con l'Element corrente — se il tipo e la Key coincidono, l'Element viene aggiornato e State rimane lo stesso.

Lo stato viene modificato solo tramite la chiamata setState, che notifica al framework la necessità di una ricostruzione. È importante capire: setState non modifica automaticamente lo stato — segna solo il widget come “sporco”. Lo sviluppatore aggiorna autonomamente i campi di State nel callback passato a setState. Dopo il completamento del callback, Flutter chiama build e aggiorna l'interfaccia.

Secondo il team Dart/Flutter (Dart Language Specification, 2026), questa separazione garantisce che tutte le modifiche allo stato avvengano in modo sincrono prima della chiamata a build, eliminando la situazione in cui l'interfaccia mostra dati parzialmente aggiornati. Questo è un meccanismo chiave di coerenza dell'interfaccia in Flutter.

Esempi di codice Dart

Consideriamo un semplice StatefulWidget — un contatore di clic su pulsante. Mostra il pattern di base: creazione di State, inizializzazione di un campo in initState, modifica tramite setState:

dart
class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Incrementa'),
        ),
      ],
    );
  }
}

Un esempio con caricamento asincrono dei dati e gestione del ciclo di vita. StatefulWidget carica dati dalla rete e mostra lo stato di caricamento:

dart
class UserProfilePage extends StatefulWidget {
  final String userId;
  const UserProfilePage({super.key, required this.userId});

  @override
  State<UserProfilePage> createState() => _UserProfilePageState();
}

class _UserProfilePageState extends State<UserProfilePage> {
  UserModel? _user;
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadUser();
  }

  Future<void> _loadUser() async {
    final user = await UserService.fetchUser(widget.userId);
    setState(() {
      _user = user;
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) return const CircularProgressIndicator();
    return Text('Ciao, ${_user!.name}');
  }
}

Nel secondo esempio, è importante notare: initState avvia un'operazione asincrona, ma il metodo stesso non è asincrono. L'asincronicità viene implementata tramite async/await all'interno di un metodo separato _loadUser, che aggiorna lo stato tramite setState dopo il completamento della richiesta. Questo approccio garantisce che il widget visualizzi correttamente l'indicatore di caricamento prima di ricevere i dati.

StatefulWidget vs StatelessWidget

La scelta tra StatefulWidget e StatelessWidget non riguarda solo la presenza dello stato. StatefulWidget fornisce un ciclo di vita completo con i metodi initState, didChangeDependencies, didUpdateWidget e dispose, necessari per lavorare con controller, animazioni e flussi. StatelessWidget, d'altra parte, non ha questi metodi ed è sempre più leggero per il framework.

La raccomandazione del team Flutter (Flutter docs, 2026) è di ridurre al minimo il numero di StatefulWidget in un'applicazione, sollevando lo stato verso l'alto nell'albero (State Hoisting) o utilizzando soluzioni di gestione dello stato (Riverpod, Bloc, Provider). Ogni StatefulWidget crea un oggetto State che vive fino alla rimozione dell'elemento — più sono questi widget, maggiore è il carico di memoria.

CriterioStatefulWidgetStatelessWidget
StatoMutabileImmutabile
Ciclo di vita6 fasiSolo build
Oggetto StateCreato separatamenteNon necessario
setStateDisponibileNon disponibile
AbbonamentiinitState/disposeNon supportati
Costruttore constLimitatoCompletamente supportato
Consumo di memoriaMaggioreMinore

Prestazioni e ottimizzazione

StatefulWidget richiede più risorse di StatelessWidget a causa della necessità di creare e mantenere un oggetto State. Tuttavia, l'uso corretto di StatefulWidget non causa problemi di prestazioni se si seguono alcune regole. Primo, evita l'annidamento profondo di StatefulWidget — ogni livello aggiunge overhead all'attraversamento dell'albero. Secondo, dividi un StatefulWidget complesso in diversi widget semplici, ognuno responsabile della propria parte di stato.

Secondo la ricerca sulle prestazioni di Flutter (Flutter.dev, febbraio 2026), la causa più comune di cali di FPS è chiamare setState in un widget genitore che ricostruisce tutti i discendenti, inclusi StatelessWidget che non hanno modificato la loro visualizzazione. La soluzione è estrarre la parte mutabile dell'interfaccia in un StatefulWidget separato in modo che setState ricostruisca solo i widget minimamente necessari.

Usare const all'interno di State è un'altra tecnica importante. Se i widget figli vengono dichiarati come const, Flutter non li ricostruirà quando setState viene chiamato nel genitore. Ciò riduce il carico sul framework e diminuisce il tempo di rendering del frame.

Evita setState frequenti

Ogni chiamata setState attiva una ricostruzione completa del widget. Se lo stato cambia con alta frequenza (ad esempio, animazione o flusso di dati), considera l'utilizzo di AnimatedBuilder, ValueListenableBuilder o StreamBuilder invece di chiamare setState manualmente. Questi widget ottimizzano la ricostruzione, aggiornando solo la parte dell'interfaccia che è effettivamente cambiata.

Errori comuni

Il primo errore comune con StatefulWidget è chiamare setState dopo dispose. Quando un widget viene rimosso dall'albero, State è considerato morto e qualsiasi chiamata a setState genera un'eccezione “setState called after dispose”. Ciò accade più spesso quando un'operazione asincrona termina dopo la rimozione del widget. La soluzione è verificare il flag mounted prima di chiamare setState o annullare le operazioni asincrone in dispose.

Il secondo errore è eseguire calcoli pesanti nel metodo build. Poiché build viene chiamato ad ogni setState e ad ogni ricostruzione del genitore, tutti i calcoli dovrebbero essere il più leggeri possibile. Se è necessaria un'operazione intensiva in termini di risorse, spostala in un Isolate separato o memorizza nella cache il risultato in un campo di State.

Il terzo errore è non chiamare super.initState() e super.dispose(). Quando si sovrascrivono questi metodi, lo sviluppatore deve chiamare l'implementazione del genitore. In caso contrario, il framework non sarà in grado di gestire correttamente lo stato di Element, portando a bug difficili da tracciare.

Raccomandazioni per evitare errori

  • Controlla sempre mounted prima di setState nei callback asincroni
  • Non dimenticare di chiamare super.initState() e super.dispose()
  • Non effettuare richieste HTTP direttamente in build — usa initState
  • Annulla tutti gli abbonamenti in dispose
  • Usa un numero minimo di StatefulWidget nel tuo progetto

Domande frequenti

In cosa si differenzia StatefulWidget da StatelessWidget?

StatefulWidget può modificare il suo stato tramite setState, ha un ciclo di vita (initState, dispose) e crea un oggetto State separato. StatelessWidget non può modificare lo stato e non ha metodi di ciclo di vita — visualizza semplicemente i dati ricevuti.

Quante volte viene chiamato createState?

createState viene chiamato esattamente una volta per ogni istanza di StatefulElement. Anche se il genitore si ricostruisce più volte, finché il tipo e la Key del widget non cambiano, createState non viene chiamato — viene utilizzato l'oggetto State esistente.

Cosa succede se non si chiama dispose?

Le risorse non verranno liberate: i controller continueranno a funzionare in background, gli abbonamenti ai flussi rimarranno attivi, i timer non verranno annullati. Ciò porta a perdite di memoria e può causare chiamate setState dopo dispose, che generano un'eccezione.

StatefulWidget può essere const?

Sì, il costruttore di StatefulWidget può essere const. Tuttavia, ciò non offre lo stesso vantaggio di StatelessWidget — l'oggetto State verrà comunque creato al primo inserimento. const influisce solo sul widget stesso (l'involucro leggero), non sullo State.

A cosa serve il metodo didUpdateWidget?

didUpdateWidget viene chiamato quando il genitore passa un StatefulWidget con nuovi parametri. Ciò è necessario per sincronizzare lo stato con i nuovi dati — ad esempio, se userId nei parametri è cambiato, è necessario caricare il profilo del nuovo utente.

Riepilogo

  • StatefulWidget è un widget con stato mutabile che utilizza un oggetto State separato per memorizzare i dati e gestire il ciclo di vita
  • Ciclo di vita comprende createState, initState, didChangeDependencies, build, didUpdateWidget e dispose, ognuno con il proprio scopo
  • setState è l'unico modo legittimo per notificare al framework un cambiamento di stato, dopo il quale build viene chiamato automaticamente
  • mounted è un flag che deve essere verificato prima di chiamare setState in operazioni asincrone per evitare un'eccezione dopo dispose
  • Prestazioni — StatefulWidget richiede più risorse di StatelessWidget; si raccomanda di minimizzarne il numero sollevando lo stato a livelli esterni
  • Widget figli const all'interno di State aiutano a ridurre la quantità di ricostruzione quando viene chiamato setState, migliorando le prestazioni
  • Scelta corretta — usa StatefulWidget solo quando il widget deve gestire dati mutabili o operazioni asincrone

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