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.
Navigazione tra le route
Navigator.pushNamed()
Naviga verso una rotta registrata:
Navigator.pushNamed(context, '/details');
Passaggio di argomenti:
Navigator.pushNamed(
context,
'/profile',
arguments: 'MarioRossi',
);
Navigator.pop()
Ritorna alla rotta precedente:
Navigator.pop(context);
Può anche restituire un valore alla rotta precedente:
Navigator.pop(context, 'Risultato di ritorno');
Navigator.pushReplacementNamed()
Sostituisce la rotta corrente con una nuova rotta nominata:
Navigator.pushReplacementNamed(
context,
'/profile',
arguments: 'MarioRossi',
);
Navigator.pushNamedAndRemoveUntil()
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,
);
Navigator.canPop()
Verifica se è possibile tornare indietro:
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
Considerazioni Importanti:
pushReplacementNamed()
epushNamedAndRemoveUntil()
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.
Navigazione con Argomenti Dinamici
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
- Apri il
pubspec.yaml
e aggiungi il pacchettoflutter_web_plugins
:
dependencies:
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter
- Aggiorna il file
main.dart
per utilizzare laPath 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 oggettoUser
contenente solo il campoemail
. - L’oggetto
User
deve essere passato allaDashboardScreen
tramite il parametroarguments
dipushNamed()
. - 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 parametroUser
dallaLoginScreen
allaDashboardScreen
. 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.