Skip to Content
FlutterLezione 4 Navigazione4.2 Navigator con Named Routes

Navigator con Named Routes

Registrazione delle route

L’approccio di base consiste nel definire una mappa di rotte all’interno di MaterialApp utilizzando la proprietà routes.

void main() { runApp(MaterialApp( initialRoute: '/', routes: { '/': (context) => const HomeScreen(), '/details': (context) => const DetailScreen(), '/profile': (context) => const ProfileScreen(), }, )); }
  • initialRoute: definisce la rotta di partenza quando l’app viene avviata.
  • routes: mappa di rotte con associazione nome-widget.

Naviga verso una rotta registrata:

Navigator.pushNamed(context, '/details');

Passaggio di argomenti:

Navigator.pushNamed( context, '/profile', arguments: 'MarioRossi', );

Ritorna alla rotta precedente:

Navigator.pop(context);

Può anche restituire un valore alla rotta precedente:

Navigator.pop(context, 'Risultato di ritorno');

Sostituisce la rotta corrente con una nuova rotta nominata:

Navigator.pushReplacementNamed( context, '/profile', arguments: 'MarioRossi', );

Naviga verso una nuova rotta nominata e rimuove le rotte precedenti fino a quella specificata:

Navigator.pushNamedAndRemoveUntil( context, '/home', ModalRoute.withName('/'), );

Per rimuovere tutte le rotte precedenti:

Navigator.pushNamedAndRemoveUntil( context, '/login', (route) => false, );

Verifica se è possibile tornare indietro:

if (Navigator.canPop(context)) { Navigator.pop(context); }

Considerazioni Importanti:

  • pushReplacementNamed() e pushNamedAndRemoveUntil() sono utili per gestire flussi come il logout o la reimpostazione della navigazione.
  • canPop() è fondamentale per evitare errori in scenari in cui non esiste una rotta precedente da poppare.

Passaggio di Parametri tramite arguments

Il metodo pushNamed() accetta un parametro opzionale arguments che può essere utilizzato per passare dati alla rotta di destinazione:

Navigator.pushNamed( context, '/profile', arguments: 'MarioRossi', );

In questo caso, stiamo inviando una stringa 'MarioRossi' alla rotta /profile.


Ricezione dei Parametri nella Rotta di Destinazione

Per accedere agli argomenti passati, utilizziamo ModalRoute.of(context)?.settings.arguments:

class ProfileScreen extends StatelessWidget { @override Widget build(BuildContext context) { final username = ModalRoute.of(context)?.settings.arguments as String?; return Scaffold( appBar: AppBar(title: Text('Profilo')), body: Center( child: Text('Benvenuto, $username!'), ), ); } }

Passaggio di Oggetti Complessi

È possibile passare oggetti complessi come mappe o classi:

Navigator.pushNamed( context, '/details', arguments: { 'id': 42, 'title': 'Dettagli del Prodotto', }, );

Nella pagina di destinazione:

class DetailScreen extends StatelessWidget { @override Widget build(BuildContext context) { final args = ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?; return Scaffold( appBar: AppBar(title: Text(args?['title'] ?? 'Dettagli')), body: Center( child: Text('ID: ${args?['id']}'), ), ); } }

Consigli e Best Practices

  • Tipizzazione: Effettua il cast degli argomenti in modo sicuro (as Tipo?) per evitare errori di runtime.
  • Gestione degli Errori: Controlla sempre se gli argomenti esistono (args != null) prima di utilizzarli.
  • Strutture Complesse: Per strutture dati complesse, considera la creazione di una classe modello e il passaggio dell’oggetto tramite arguments.

Esempio di passaggio di un oggetto personalizzato:

class User { final String username; final int age; User({required this.username, required this.age}); } Navigator.pushNamed( context, '/profile', arguments: User(username: 'MarioRossi', age: 30), ); class ProfileScreen extends StatelessWidget { @override Widget build(BuildContext context) { final user = ModalRoute.of(context)?.settings.arguments as User?; return Scaffold( body: Center( child: Text('Benvenuto ${user?.username}, età: ${user?.age}'), ), ); } }

onGenerateRoute: Gestione del Routing Dinamico

onGenerateRoute consente di gestire la creazione delle rotte in modo dinamico e centralizzato. È particolarmente utile per:

  • Passare argomenti dinamici tra le rotte.
  • Definire rotte che non sono state specificate nella mappa routes.
  • Gestire animazioni personalizzate per le transizioni tra le schermate.

Implementazione Base

MaterialApp( onGenerateRoute: (settings) { if (settings.name == '/profile') { final args = settings.arguments as String; return MaterialPageRoute( builder: (context) => ProfileScreen(username: args), ); } else if (settings.name == '/details') { final data = settings.arguments as Map<String, dynamic>; return MaterialPageRoute( builder: (context) => DetailScreen(id: data['id'], title: data['title']), ); } return MaterialPageRoute( builder: (context) => const NotFoundPage(), ); }, );
  • settings.name: identifica il nome della rotta.
  • settings.arguments: consente di passare dati tra le schermate.

Navigator.pushNamed( context, '/profile', arguments: 'MarioRossi', ); Navigator.pushNamed( context, '/details', arguments: {'id': 42, 'title': 'Prodotto XYZ'}, );

Nella schermata di destinazione:

class ProfileScreen extends StatelessWidget { final String username; ProfileScreen({required this.username}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text('Benvenuto, $username!'), ), ); } }

onUnknownRoute: Gestione delle Rotte Non Definite

onUnknownRoute viene invocato quando il Navigator non trova una rotta registrata o gestita tramite onGenerateRoute. È utile per:

  • Visualizzare una pagina di errore o 404.
  • Reindirizzare l’utente a una schermata specifica.

Implementazione Base

MaterialApp( onUnknownRoute: (settings) => MaterialPageRoute( builder: (context) => const NotFoundPage(), ), );

Esempio di Navigazione verso una Rotta Inesistente

Navigator.pushNamed(context, '/non-esiste');

In questo caso, verrà visualizzata la NotFoundPage poiché /non-esiste non è definita né in routes né in onGenerateRoute.


Consigli per l’Uso di onGenerateRoute e onUnknownRoute

  • Utilizzare onGenerateRoute per gestire il passaggio di parametri complessi o dinamici.
  • Implementare onUnknownRoute per migliorare l’UX in caso di rotte errate o mancanti.
  • Centralizzare la logica di routing in una singola funzione per migliorare la manutenibilità del codice.

Rimozione del # nell’URL per Flutter Web

Di default, Flutter Web utilizza il # nell’URL per mantenere la compatibilità con browser che non supportano il routing lato client. Tuttavia, possiamo rimuovere il # e utilizzare URL più puliti tramite la configurazione della Path URL Strategy.

Configurazione del progetto

  1. Apri il pubspec.yaml e aggiungi il pacchetto flutter_web_plugins:
dependencies: flutter: sdk: flutter flutter_web_plugins: sdk: flutter

  1. Aggiorna il file main.dart per utilizzare la Path URL Strategy:
import 'package:flutter/material.dart'; import 'package:flutter_web_plugins/url_strategy.dart'; void main() { // Rimuove il # dall'URL usePathUrlStrategy(); runApp(MyApp()); }

Esercizio - Passaggio Parametri con Named Routes

Obiettivo

Estendere l’esercizio precedente per implementare il passaggio di un oggetto User dalla LoginScreen alla DashboardScreen utilizzando le named routes.

  • Modificare la logica di OnboardingScreen da primo widget a widget che appare solo non è stato ancora completato, gestito da un widget padre di home_screen da un provider o ValueNotifier
  • La LoginScreen deve raccogliere l’email e creare un oggetto User contenente solo il campo email.
  • L’oggetto User deve essere passato alla DashboardScreen tramite il parametro arguments di pushNamed().
  • La DashboardScreen deve ricevere e visualizzare l’email dell’utente o un testo “Parametro non passato” se nullo.

Struttura dell’oggetto User

class User { final String email; User({required this.email}); }

Requisiti

  • Implementare la navigazione utilizzando named routes.
  • Utilizzare il costruttore pushNamedAndRemoveUntil() per il passaggio del parametro User dalla LoginScreen alla DashboardScreen. Questo permetterà di rimuovere tutte le rotte precedenti e prevenire il ritorno alla schermata di login una volta effettuato l’accesso.
  • Gestire il caso in cui il parametro User sia nullo.