Anche in una semplice applicazione Flutter la gestione della navigazione tra le varie pagine è uno dei primi problemi che occorre spesso affrontare. Ad esempio, in uno dei post precedenti per mostrare differenti tipi di widget ho usato una pagina principale con una serie di pulsanti che rimandavano alle singole pagine. Nel post di oggi vedremo due semplici approcci a questo problema.
Il metodo imperativo
In Flutter 1.0 la navigazione tra pagine utilizza sostanzialmente una struttura LIFO (Last In First Out) di widget in cui ogni route (pagina) può essere collocata in cima o rimossa dalla cima utilizzando la classe Navigator e specificamente i suoi metodi .push e .pop.Per un esempio, prepariamo innanzitutto un nuovo progetto Flutter che preveda almeno due rotte, la prima con un pulsante che rimanda alla seconda e la seconda con un pulsante che permette di tornare indietro alla prima pagina.
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
title: 'Test navigazione',
home: Main(),
));
}
class Main extends StatelessWidget {
const Main({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Prima pagina'),
),
body: Center(
child: MaterialButton(
color: Colors.red,
child: const Text('Pagina 2'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondPage()),
);
},
),
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Seconda pagina"),
),
body: Center(
child: MaterialButton(
color: Colors.amber,
onPressed: () {
Navigator.pop(context);
},
child: const Text('Indietro!'),
),
),
);
}
}
Il metodo .push (che richiede due argomenti, il BuildContext e un PageBuilder), è utilizzato per mettere in cima allo stack la seconda pagina, mentre il metodo .pop (che richiede come unico argomento il BuildContext) è utilizzato per rimuovere l'ultimo elemento inserito nel stack (la seconda pagina) e quindi tornare alla prima.
Le rotte utilizzate nell'esempio sono rotte anonime, ma è possibile anche assegnare dei nomi alle rotte, anziché le classi, come nell'esempio successivo:
runApp(MaterialApp(
title: 'Test navigazione',
home: Main(),
routes: {
"seconda": (context) => const SecondPage()
},
));
e usare il metodo .pushNamed al posto di .push (il metodo .pop rimane lo stesso)
child: MaterialButton(
color: Colors.red,
child: const Text('Pagina 2'),
onPressed: () {
Navigator.pushNamed(context, "seconda");
},
),
Il metodo dichiarativo
Con Flutter 2.0 la navigazione tra pagine è diventata una funzione associata allo stato: il cambio di pagina può quindi essere gestito modificando lo stato dell'applicazione. Se proviamo a riscrivere il codice precedente usando un widget StatefulWidget ed aggiungendo anche una terza pagina, otteniamoimport 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
title: 'Test navigazione',
home: Main(),
));
}
class Main extends StatefulWidget {
const Main({Key? key}) : super(key: key);
@override
State<Main> createState() => _MainState();
}
class _MainState extends State<Main> {
String _selected="prima";
Page _buildMain(){
return MaterialPage(
child: Scaffold(
appBar: AppBar(
title: const Text('Prima pagina'),
),
body: Center(
child: Column(
children: [
MaterialButton(
color: Colors.red,
child: const Text('Pagina 2'),
onPressed: (){
setState(() {
_selected="seconda";
});
},
),
MaterialButton(
color: Colors.red,
child: const Text('Pagina 3'),
onPressed: (){
setState(() {
_selected="terza";
});
},
),
],
),
),
),
key: const ValueKey("prima")
);
}
Page _buildSecondPage(){
return MaterialPage(
child: Scaffold(
appBar: AppBar(
title: const Text("Seconda pagina"),
),
body: Center(
child: Column(
children: [
MaterialButton(
color: Colors.amber,
onPressed: (){
setState(() {
_selected="prima";
}
);
},
child: const Text('Pagina 1'),
),
MaterialButton(
color: Colors.amber,
onPressed: (){
setState(() {
_selected="terza";
}
);
},
child: const Text('Pagina 3'),
),
],
),
),
),
key: const ValueKey("seconda")
);
}
Page _buildThirdPage(){
return MaterialPage(
child: Scaffold(
appBar: AppBar(
title: const Text("Terza pagina"),
),
body: Center(
child: Column(
children: [
MaterialButton(
color: Colors.pink,
onPressed: (){
setState(() {
_selected="prima";
}
);
},
child: const Text('Pagina 1'),
),
MaterialButton(
color: Colors.pink,
onPressed: (){
setState(() {
_selected="seconda";
}
);
},
child: const Text('Pagina 2'),
),
],
),
),
),
key: const ValueKey("terza")
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Navigator(
onPopPage: (route, result) => route.didPop(result),
pages: [
_buildMain(),
if (_selected=="seconda") _buildSecondPage(),
if (_selected=="terza") _buildThirdPage(),
],
),
);
}
}
Con questo tipo di scrittura il cambio pagina si ottiene perché modificando la variabile _selected
si scatena un cambio di stato e il metodo build si occupa di visualizzare la pagina selezionata.
La navigazione non avviene più in modo lineare (in avanti con il push e indietro con il pop), ma può avvenire in qualsiasi ordine.
Da notare come con String _selected="prima"; impostiamo la rotta iniziale.
Sicuramente nei prossimi articoli torneremo sulla navigazione e sulle rotte, introducendo altri elementi necessari per riuscire a gestire situazioni più complesse.
Come per gli articoli precedenti potete trovare il codice completo tra le mie repo GitHub:
Buon lavoro !
Commenti
Posta un commento