Nelle lezioni precedenti, abbiamo analizzato in dettaglio i possibili approcci per navigare tra le schermate di un'applicazione così da permettere all'utente di scoprire le funzionalità disponibili e i dati forniti.
Come tutte le applicazioni moderne a cui siamo abituati, un aspetto utile è lo scambio di informazioni tra due schermate. La condivisione dei dati è, infatti, molto importante durante la navigazione dell'utente in quanto gli permette di interagire con nuove informazioni a partire dalle prime. Ad esempio, quando l'utente interagisce con una lista di elementi e clicca su uno di questi, l'informazione relativa all'identificativo univoco dell'elemento viene inoltrata alla schermata successiva per estrarre l'entità dal DB e popolare la schermata di dettaglio.
In questa e nella prossima lezione vedremo nel dettaglio tre diversi approcci per condividere dati durante la navigazione attraverso esempi pratici ed intuitivi.
Scambio dati tra due schermate tramite MaterialRoutePage
Vediamo alcuni esempi pratici per lo scambio dati tra due schermate di un'applicazione contestualmente alla navigazione, sfruttando proprio i componenti Route, Navigator e MaterialRoutePage con cui abbiamo familiarizzato nella lezione precedente.
Come di consueto, 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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: /*set home*/,
}
}
Prima di proseguire con gli esempi, definiamo una classe di oggetti che utilizzeremo come dato da passare tra due schermate. In particolare, andremo a definire la classe Article che rappresenta un articolo composto da due proprietà:
- un titolo;
- una descrizione.
Per farlo basterà definire la classe come segue.
class Article {
final String title;
final String description;
Article(this.title, this.description);
}
In particolare, abbiamo definito all'interno della classe un costruttore che accetta come parametri di ingresso il titolo e la descrizione.
Per semplicità abbiamo messo questa classe all'interno del file main.dart, ma è opportuno definire il modello di dominio all'interno di un apposito file .dart contenente le entità che rappresentano il dominio della nostra applicazione. L'organizzazione del codice sarà oggetto delle prossime lezioni.
Definiamo adesso due schermate:
- una che chiameremo
HomePage, che mostrerà un semplice bottone e inoltrerà i dati alla seconda schermata; - una schermata di dettaglio,
ArticleDetailPage, che mostrerà le informazioni (titolo e descrizione) dell'articolo selezionato dall'utente.
Vediamo come definire le due classi e partiamo dalla HomePage.
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: RaisedButton(
onPressed: () {ì
},
child: Text('Open ArticleDetailPage'),
),
),
);
}
}
In questo modo, abbiamo creato un nuovo StatelessWidget composto da:
- uno
Scaffold; - un
AppBar; - un
RaisedButtonposto al centro della schermata.
Quando l'utente cliccherà sul bottone, aprirà la pagina di dettaglio che può essere definita così.
class ArticleDetailPage extends StatelessWidget {
final Article article;
ArticleDetailPage({Key key, this.article}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(article.title),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Text(article.description),
),
);
}
}
In particolare abbiamo:
- definito la proprietà
articledi tipoArticle; - definito il costruttore per la classe
ArticleDetailPageche può prendere in input un oggetto di tipoArticle; - sovrascritto il metodo
buildaffinché il titolo dell'AppBarsia impostato alla proprietà title definita nell'oggettoarticle, ed è stato definito un widgetTextcontenente la descrizione dell'articolo.
Non resta che definire la callback per la proprietà RaisedButton.onPressed della HomePage che:
- definirà un nuovo oggetto
Articlecomposto da un titolo e una descrizione; - definirà un nuovo widget
Routeusando ilMaterialPageRoutewidget visto nella precedente lezione; - invocherà il metodo
Navigator.pushper comunicare al framework di navigare verso la nuova rotta.
Riportiamo di seguito una possibile implementazione della callback così definita.
onPressed: () {
Article article =
new Article('Article', 'This is a simple text for Article');
Route route = MaterialPageRoute(
builder: (context) => ArticleDetailPage(article: article),
);
Navigator.push(context, route);
},
Impostiamo la proprietà MaterialApp.home della classe MyApp alla classe HomePage
home: HomePage(),
ed eseguiamo l'applicazione per vedere i risultati delle nostre modifiche.
Come si vede in figura, cliccando sul bottone presente nella prima schermata, l'utente crea un nuovo Article e inoltra l'oggetto alla rotta che sostituisce la schermata della HomePage con la rotta modale contenente il widget ArticleDetailPage, che mostra i dettagli dell'articolo.
Complichiamo leggermente lo scenario e immaginiamo di avere una lista di articoli da visualizzare e da cui possiamo sceglierne uno per visualizzare un dettaglio.
Per farlo, dovremo creare nella nostra app una nuova schermata in cui visualizzare una lista predefinita e popolata staticamente. Vediamo come con il seguente esempio.
ArticleListPage({Key key, @required this.articles}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
body: ListView.builder(
itemCount: articles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(articles[index].title),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ArticleDetailPage(article: articles[index]),
),
);
},
);
},
),
);
}
}
In questo caso abbiamo definito:
- un nuovo widget,
ArticleListPage, avente la proprietàarticlesche definisce una lista di oggettiArticleda mostrare all'utente; - definito il costruttore della classe che prende come input (obbligatorio) una lista di articoli;
- all'interno del metodo
ArticleListPage.buildunoScaffoldcontenente l'AppBare comebodyuna nuovaListViewcostruita a partire dalla proprietàarticles.
Abbiamo inoltre definito la proprietà ListTile.onTap (di cui abbiamo discusso nella lezione 18) in modo che, cliccando su un elemento della lista, la callback invochi il metodo Navigator.push per rimandare l'utente alla pagina di dettaglio con le nuove informazioni passate al costruttore del widget ArticleDetailPage.
Adesso non resta che creare la nostra lista statica e modificare la classe MyApp come segue.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// . . .
home: ArticleListPage(
articles: List.generate(
20,
(i) =>
Article(
'Article $i',
'This is a simple text for Article $i',
),
),
),
);
}
}
In questo caso abbiamo impostato la proprietà home con la classe ArticleListPage e assegnato alla proprietà ArticleListPage.articles una lista generata dinamicamente tramite il metodo List.generate, che crea una lista di 20 articoli composti da un titolo e una descrizione.
Non resta che eseguire l'applicazione per vedere i risultati delle modifiche effettuate.


Come si può notare, quando l'utente tocca uno specifico elemento della lista, la callback della proprietà onTap viene invocata dal framework per ridirezionare l'utente alla pagina di dettaglio tramite il Navigator. Una volta atterrato sulla pagina di dettaglio l'utente vede il titolo e la descrizione dell'articolo selezionato.
Questo semplice esempio non ne fa uso, ma è possibile estendere quanto fatto alle rotte nominali.
Il codice di questa lezione è disponibile su GitHub.
Se vuoi aggiornamenti su Mobile inserisci la tua email nel box qui sotto:

