Ora che abbiamo un quadro più chiaro dei metodi offerti dalla classe StatefulWidget e dalla classe State, e dopo aver compreso il ciclo di vita di quest’ultimo, vediamo un esempio pratico di come creare uno stateful widget a partire da uno stateless.
Esempio Pratico
Creiamo un nuovo progetto come illustrato nella lezione 6 di questa guida e cancelliamo tutto eccetto la classe MyApp, definita come segue.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Lesson 24',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(title: Text('Lesson 24')),
body: Center(child: MyWidget(traveler: "Antonio", country: "Finland")),
)
);
}
}
In particolare possiamo notare che la proprietà body dello Scaffold ha come valore il widget MyWidget, definito come segue.
class MyWidget extends StatelessWidget {
final String traveler;
final String country;
MyWidget({Key key, this.traveler, this.country}): super(key:key);
@override
Widget build(BuildContext context) {
return Text('$traveler visited $country');
}
}
Lo scopo di questo widget è quello di mostrare a schermo un testo contenente una stringa che rappresenta un viaggiatore e la nazione che ha visitato.
Eseguendo l’applicazione, infatti, otterremo il seguente risultato.
Immaginiamo adesso di voler trasformare questo widget in modo che l’utente possa inserire la nazione in modo dinamico modificando il testo gestito dal Text widget. Per farlo dovremo compiere i seguenti passi:
- estendere la classe
StatefulWidget, che restituisce un oggettoStatetramite il metodocreateState. Questa classe inoltre gestirà solo ed esclusivamente la proprietàtraveler; - creare una classe che estenda la classe
State, la quale dovrà sovrascrivere il metodoState.builde gestire la variabilecountryaggiornabile tramite input dall’utente; - invocare il metodo
State.setStateper modificare lo stato e scatenare una ricostruzione del widget (e del suo sottoalbero).
Partiamo dal primo passo e modifichiamo la classe MyWidget affinché estenda la classe StatefulWidget come segue.
class MyWidget extends StatefulWidget {
// . . .
}
Successivamente, rimuoviamo dalla classe la proprietà country aggiornando di conseguenza il costruttore e implementiamo il metodo createState offerto dalla classe StatefulWidget e che obbligatoriamente dobbiamo implementare. Al termine delle modifiche otterremo il seguente risultato.
class MyWidget extends StatefulWidget {
final String traveler;
MyWidget({Key key, this.traveler}): super(key:key);
@override
State<StatefulWidget> createState() => _MyWidgetState();
}
In particolare, il metodo createState restituirà un’istanza della classe privata _MyWidgetState che definirà lo State del nostro stateless widget e la UI.
Passiamo quindi al secondo punto e creiamo la classe _MyWidgetState che dovrà:
- gestire la variabile
countryrimossa dalla classeMyWidget; - sovrascrivere il metodo
buildper rappresentare la nuova interfaccia composta da due widget: unTextFieldper permettere all’utente di inserire un input; unTextche mostri il testo del widget.
Di seguito l’implementazione.
class _MyWidgetState extends State<MyWidget> {
String country='';
@override
Widget build(BuildContext context) {
return
Container(
child:Column(
children: <Widget>[
TextField(
onSubmitted: (String countyName){
country = countyName;
},
),
Divider(),
Text('${widget.traveler} visited $country'),
],
),
width: 200,
height: 200,
);
}
}
In questa classe abbiamo istanziato la proprietà country ad una stringa vuota, successivamente abbiamo definito un Container composto a sua volta da un Column widget, che definisce il TextField, un Divider ed il Text widget.
Più nello specifico abbiamo definito la proprietà onSubmitted del TextField affinché ogni volta che l’utente inserisce un input questo venga associato alla proprietà country, mentre per il Text widget abbiamo effettuato l’accesso al valore delle proprietà:
country, gestita direttamente dalla classe_MyWidgetState;widget.traveler, ossia alla proprietà definita nello stateful widgetMyWidgeta cui possiamo accedere grazie alla proprietàState.widgetche gestisce il riferimento aMyWidget.
Aggiorniamo quindi la proprietà body dello Scaffold definito nella classe MyApp come segue.
body: Center(child: MyWidget(traveler: "Antonio")),
Abbiamo quindi passato allo stateful widget MyWidget solo la proprietà traveler poichè la country viene gestita tramite input. Eseguiamo l’applicazione per ottenere il risultato in figura.

Come possiamo notare, nonostante sia stato inserito il testo Finland nel TextField, l’informazione non viene mostrata nel Text widget in quanto non è cambiato lo stato dell’oggetto State associato allo stateful element.
Per risolvere questo inconveniente possiamo passare al terzo punto del nostro elenco, ossia invocare il metodo State.setState per modificare lo stato e scatenare una ricostruzione del widget.
Per farlo, dobbiamo semplicemente modificare il valore della proprietà TextField.onSubmitted come segue:
onSubmitted: (String countyName){
setState(() {
country = countyName;
});
},
Così facendo stiamo aggiornando lo stato scatenando la ricreazione del widget come mostrato nella seguente figura.
In questo modo, ogni volta che inseriremo un nuovo testo verrà aggiornato finalmente anche il Text widget con il nuovo valore inserito.
Dietro le quinte
Schematicamente, quello che accade è rappresentato dalla seguente figura.

Alla creazione del widget nel widget tree, viene richiesta dal framework la creazione di uno stateful element relativo al widget. A questo punto lo stateful element richiede al widget la creazione di un oggetto State ed è proprio in questo caso che viene invocato il metodo StatefulWidget.createState, che abbiamo sovrascritto nella classe MyWidget. Questo metodo quindi crea un nuovo oggetto MyWidgetState che sarà preso dallo stateful element. A questo punto, il MyWidgetState invocherà il metodo build per costruire l’interfaccia e per farlo avrà bisogno proprio delle due proprietà di interesse, ossia traveler fornita da MyWidget e country gestita dall’oggetto _MyWidgetState. A questo punto viene inserito all’interno del sottoalbero di MyWidget lo stateless widget Text contenente la stringa corretta, e nell’element tree verrà montato un nuovo stateless element.
Nel momento in cui l’utente inserirà una nuova nazione visitata, verrà invocato il metodo setState che aggiorna il valore country gestito da MyWidgetState. A questo punto l’oggetto State marcherà il suo stateful element come dirty, richiedendo quindi la ricreazione dei suoi widget figli nel frame successivo. Con il frame successivo, lo stateful element invocherà il metodo MyWidgetState.build per ricostruire il widget e mostrare il nuovo testo attraverso il Text widget. Il vecchio Text widget verrà quindi distrutto e sostituito dal nuovo e, proprio perché il nuovo widget ha lo stesso runtimeType e Widget.key del precedente, lo stateless element non verrà rimosso e aggiornerà la sua referenza al nuovo widget come mostrato in figura.
Considerazioni
Abbiamo visto un esempio semplice ma efficace per comprendere appieno quello che succede nel ciclo di vita di uno stateful widget. Ciononostante, imparando a conoscere sempre di più questo framework, le occasioni in cui andremo ad utilizzare uno stateful widget saranno sempre di meno, in quanto molti dei principali casi d’uso in cui utilizzarli sono già implementati dal framework stesso. Un esempio è proprio offerto dalla classe StreamBuilder, che ricostruisce il widget ogni volta che l’oggetto Stream fornisce un nuovo valore.
Il codice di questa lezione è disponibile su Github.
Se vuoi aggiornamenti su Mobile inserisci la tua email nel box qui sotto:



