Skip to Content

Navigazione

Perchè diventa necessaria

Con l’aumentare della complessità e del numero di schermate necessarie da mostrare all’utente nasce l’esigenza di creare una navigazione all’interno dell’applicazione. Questo può essere fatto in diverse maniere dalla più semplice e immediata alla più complessa. Flutter ci fornisce un sistema di navigazione completo attraverso l’oggetto Navigator

Piccolo passo indietro

Abbiamo visto in precedenza una modalità semplice per mostrare una pagina selezionata con l’uso di una NavigationBar e il setState().

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { List<Widget> pages = [ Center(child: Text('Hello, Home page')), Center(child: Text('Hello, Profile page')), ]; int currentIndex = 0; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: pages[currentIndex], bottomNavigationBar: NavigationBar( selectedIndex: currentIndex, onDestinationSelected: (index) => setState(() { currentIndex = index; }), destinations: [ NavigationDestination(icon: Icon(Icons.home), label: 'Home'), NavigationDestination(icon: Icon(Icons.person), label: 'Profile'), ], ), ), ); } }

Il sistema di navigazione di Flutter si basa su uno stack, una pila di schermate che costituisce la cronologia di navigazione con la possibilità di tornare indietro tra le schermate navigate.

In Flutter, puoi navigare tra schermate usando Navigator. I metodi più basilari e usati sono:

  • push(): per aprire una nuova schermata.
  • pop(): per tornare indietro alla schermata precedente.
⚠️
Warning

Quando usiamo il metodo .pop() bisogna fare attenzione che esista una schermata precedente.
Ci aiuta il metodo .canPop() che ci restituisce se è possibile richiamare il metodo pop in sicurezza. In caso contrario l’applicazione andrebbe in errore.

Esempio

import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Demo Navigazione', theme: ThemeData(useMaterial3: true), home: const HomeScreen(), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Home')), body: Center( child: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const SecondScreen()), ); }, child: const Text('Vai alla Seconda Schermata'), ), ), ); } } class SecondScreen extends StatelessWidget { const SecondScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Seconda Schermata')), body: Center( child: ElevatedButton( onPressed: () { Navigator.pop(context); }, child: const Text('Torna alla Home'), ), ), ); } }
Note

Anche il Dialog viene chiuso attraverso il metodo .pop()

Push Replacement

Nel routing di Flutter, Navigator.pushReplacement(...) ti permette di navigare verso una nuova schermata eliminando quella attuale dallo stack di navigazione. È una funzione utile per situazioni in cui non vuoi che l’utente possa tornare indietro.

Quando usare pushReplacement

  • Dopo il login: Quando un utente effettua il login, non vuoi che possa tornare indietro alla schermata di login.
  • Dopo uno splash screen: Dopo aver mostrato una schermata iniziale, passi all’app vera e propria
  • Dopo un logout: Ritorni alla schermata di login, eliminando quella corrente

pushAndRemoveUntil()

Il metodo pushAndRemoveUntil() in Flutter è utilizzato per inserire una nuova schermata nello stack di navigazione e rimuovere tutte le schermate precedenti fino a una condizione specificata. È particolarmente utile quando si vuole evitare che l’utente possa tornare indietro a determinate schermate dopo un’azione (ad esempio, dopo un login o un logout).

Sintassi

Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => NuovaSchermata()), (Route<dynamic> route) => condizione, );
  • context – Il contesto della schermata corrente.
  • MaterialPageRoute – La nuova schermata che verrà visualizzata.
  • condizione – Una funzione che restituisce un booleano per determinare quali route devono essere mantenute nello stack.

Esempio Pratico: Login con pushAndRemoveUntil()

Supponiamo di avere una schermata di login (LoginScreen). Una volta eseguito il login, vogliamo portare l’utente alla DashboardScreen e rimuovere tutte le schermate precedenti.

void login(BuildContext context) { Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => DashboardScreen()), (Route<dynamic> route) => false, ); }
  • (Route<dynamic> route) => false – Questa condizione rimuove tutte le route precedenti nello stack.

Condizioni Personalizzate

Puoi anche specificare una condizione che mantenga alcune schermate nello stack. Ad esempio, se vuoi mantenere solo la HomeScreen:

void navigateToDashboard(BuildContext context) { Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => DashboardScreen()), (Route<dynamic> route) => route.settings.name == '/home', ); }
  • In questo caso, la HomeScreen rimarrà nello stack e l’utente potrà tornare indietro ad essa.

Differenze tra i metodi di navigazione con Navigator

MetodoComportamento
push()Aggiunge una nuova schermata sopra la corrente (back disponibile)
pushReplacement()Sostituisce la schermata corrente con una nuova (no back)
pushAndRemoveUntil()Rimuove una o più schermate precedenti, secondo una condizione

MaterialPageRoute

MaterialPageRoute è una classe di Flutter che implementa una transizione in stile Material Design tra schermate. Viene usata per creare una nuova “route” (pagina) da inserire nello stack di navigazione.

Note

Una Route rappresenta una schermata o una pagina in Flutter. Può essere gestita da Flutter in modo implicito (MaterialPageRoute) o personalizzato (PageRouteBuilder)

È il modo più comune per aggiungere un comportamento di navigazione da una schermata all’altra nelle app Flutter, ed è utilizzata con Navigator.push, Navigator.pushReplacement o Navigator.pushAndRemoveUntil.

Caratteristiche:

  • Mostra animazioni predefinite di transizione (slide da destra su Android, modale su iOS).
  • È costruita appositamente per rispettare le linee guida Material Design.
  • Ha proprietà utili come fullscreenDialog per presentazioni in stile modale.

fullscreenDialog: presentazione in stile modale

Puoi usare fullscreenDialog: true in MaterialPageRoute per aprire una schermata in stile modale, utile ad esempio per schermate di inserimento o moduli.

Esempio:
Navigator.push( context, MaterialPageRoute( builder: (context) => const CreateItemScreen(), fullscreenDialog: true, ), );
Note

Questo cambia l’animazione e l’icona “back” (diventa una close), per indicare che la schermata non fa parte della navigazione lineare.

Esiste inoltre PageRouteBuilder per inserire animazioni personalizzate come Fade.

Passaggio dati tra le schermate (routes)

Possiamo recuperare dati tra le schermate anche attraverso uno stato centrale (es. Provider), ma Flutter consente anche di passare parametri direttamente ai costruttori delle schermate required o no.

Esempio

Navigator.push( context, MaterialPageRoute( builder: (context) => const SecondScreen(title:'Titolo passato'), fullscreenDialog: true, ), ); class SecondScreen extends StatelessWidget{ SecondScreen({ super.key, required this.title }); String title; @override Widget build(context){ return Scaffold( appBar:AppBar( title:Text(title), ), ); } }
Note

Nel caso di StatefulWidget possiamo ottenere accesso al dato passato attraverso l’attributo widget richiamabile dentro la classe che estende State<>

Recupero di un parametro passato attraverso il metodo .pop()

// Vai alla seconda schermata e attendi un risultato final result = await Navigator.push( context, MaterialPageRoute( builder: (_) => const SecondScreen(), ), ); // Usa il risultato print('Risultato: $result'); // Dentro la SecondScreen onPressed: () { Navigator.pop(context, 'Risultato da SecondScreen'); }

Esercizio - Implementazione della Navigazione in Flutter

Obiettivo

Creare un’applicazione che gestisca la navigazione tra più schermate utilizzando i metodi del Navigator. L’obiettivo è consolidare i concetti di:

  • push()
  • pop()
  • pushReplacement()
  • pushAndRemoveUntil()
  • MaterialPageRoute
  • fullscreenDialog

Aggiungere una conferma di logout tramite AlertDialog nella DashboardScreen. Implementare il dialogo in modo che l’utente possa confermare o annullare l’azione.

Creare un onboarding con tre schermate utilizzando PageView. Al termine dell’onboarding, eseguire un pushReplacement() per accedere alla HomeScreen.

Descrizione dell’Applicazione

L’applicazione sarà un semplice Sistema di Onboarding, Login e Registrazione composto da 5 schermate:

  • OnboardingScreen: Schermata iniziale composta da tre pagine utilizzando PageView. Al termine, esegue un pushReplacement() per accedere alla HomeScreen.
  • HomeScreen: Schermata iniziale con pulsanti per accedere a Login e Registrazione.
  • LoginScreen: Schermata di login con un campo email e un pulsante “Accedi”.
  • RegistrationScreen: Schermata di registrazione con un campo email e un pulsante “Registra”.
  • DashboardScreen: Schermata di benvenuto dopo il login o la registrazione.

Requisiti

OnboardingScreen

  • Tre pagine di onboarding gestite con PageView.
  • Un pulsante “Inizia” al termine dell’ultima pagina che esegue un pushReplacement() per accedere alla HomeScreen.

HomeScreen

  • Due pulsanti: “Login” e “Registrati”.
  • La schermata di login utilizzerà push() per aprire LoginScreen.
  • La schermata di registrazione utilizzerà fullscreenDialog: true.

LoginScreen

  • Un campo di testo per inserire l’email.
  • Un pulsante “Accedi” che utilizza pushAndRemoveUntil() per accedere al DashboardScreen.

RegistrationScreen

  • Un campo di testo per inserire l’email.
  • Un pulsante “Registra” che utilizza pushAndRemoveUntil() per accedere al DashboardScreen.

DashboardScreen

  • Un pulsante “Logout” che apre un AlertDialog per confermare l’azione. Se confermato, utilizza pushAndRemoveUntil() per tornare alla HomeScreen. Se annullato, chiude il dialogo senza eseguire azioni.