4.2Navigation entre écrans
4.2.1 – Principes de la navigation dans Flutter
La navigation dans Flutter fonctionne comme une pile (stack) : chaque nouvel écran est empilé au-dessus du précédent, et vous pouvez revenir en arrière en retirant l'écran du haut de la pile.
📚 Qu'est-ce que la navigation ?
La navigation permet de passer d'un écran à un autre dans votre application. Par exemple, quand vous cliquez sur un bouton "Détails", vous naviguez vers un nouvel écran qui affiche plus d'informations.
Imaginez une pile de cartes. Chaque fois que vous ouvrez un nouvel écran, vous ajoutez une carte sur la pile. Quand vous appuyez sur "Retour", vous retirez la carte du haut pour revenir à la précédente.
🔑 Concepts clés
- Navigator : Le widget qui gère la navigation dans Flutter
- Route : Un écran ou une page dans votre application
- Push : Ajouter un nouvel écran sur la pile
- Pop : Retirer l'écran actuel de la pile (retour en arrière)
Navigator est un widget qui gère la pile de navigation. Il est automatiquement disponible dans chaque MaterialApp ou CupertinoApp.
📝 Syntaxe de base
Pour naviguer vers un nouvel écran, vous utilisez :
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NouvelEcran(),
),
);
Pour revenir en arrière :
Navigator.pop(context);
Le paramètre
context est nécessaire pour que Flutter sache depuis quel écran vous naviguez. Vous l'obtenez généralement dans la méthode build de votre widget.
4.2.2 – Navigator : push et pop
Navigator.push() permet d'ajouter un nouvel écran sur la pile, et Navigator.pop() permet de revenir à l'écran précédent.
➡️ Navigator.push()
Navigator.push() ajoute un nouvel écran au-dessus de l'écran actuel. L'utilisateur peut ensuite revenir en arrière avec le bouton retour.
📝 Syntaxe
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NouvelEcran(),
),
);
Analysons cette syntaxe :
context: Le contexte de l'écran actuelMaterialPageRoute: Le type de transition utilisé (animation Material)builder: (context) => NouvelEcran(): La fonction qui construit le nouvel écran
⬅️ Navigator.pop()
Navigator.pop() retire l'écran actuel de la pile et revient à l'écran précédent.
📝 Syntaxe
Navigator.pop(context);
đź§Ş Exemple : Navigation simple
Voici un exemple complet avec deux écrans :
Navigator.push et Navigator.pop pour naviguer entre deux écrans.
import 'package:flutter/material.dart';
void main() {
runApp(const MonApp());
}
class MonApp extends StatelessWidget {
const MonApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const EcranAccueil(),
);
}
}
// Écran d'accueil
class EcranAccueil extends StatelessWidget {
const EcranAccueil({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Accueil'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EcranDetails(),
),
);
},
child: const Text('Voir les détails'),
),
),
);
}
}
// Écran de détails
class EcranDetails extends StatelessWidget {
const EcranDetails({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Détails'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Vous êtes sur l\'écran de détails',
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Retour'),
),
],
),
),
);
}
}
Dans cet exemple :
- L'écran d'accueil a un bouton "Voir les détails"
- Quand vous cliquez dessus,
Navigator.push()ouvre l'écran de détails - L'écran de détails a un bouton "Retour" qui utilise
Navigator.pop()pour revenir Ă l'accueil
MaterialPageRoute crée une transition animée de type Material Design. L'écran glisse depuis la droite vers la gauche quand vous naviguez vers l'avant, et dans l'autre sens quand vous revenez en arrière.
Le bouton retour physique (ou le geste de balayage) sur Android fonctionne automatiquement avec
Navigator.pop(). Vous n'avez pas besoin de le gérer manuellement.
Lorsque vous utilisez un
AppBar avec Navigator, Flutter affiche automatiquement une flèche retour ("back button") dans l'AppBar si la pile de navigation permet un retour (Navigator.canPop(context) est vrai). Cliquer sur cette flèche appelle lui-même Navigator.pop(context), comme le fait le bouton "Retour" dans le corps de l'écran.
- Pas besoin d'ajouter de code spécifique pour le bouton retour de l'AppBar dans la plupart des cas.
- Vous pouvez personnaliser le bouton retour avec la propriété
leadingde l'AppBarsi nécessaire.
4.2.3 – Routes anonymes
Les routes anonymes sont des écrans créés directement dans le code de navigation, sans être définis à l'avance. C'est ce que nous avons utilisé dans l'exemple précédent avec MaterialPageRoute.
đź“‹ Qu'est-ce qu'une route anonyme ?
Une route anonyme est un écran créé "à la volée" lors de la navigation. Vous définissez l'écran directement dans le builder de MaterialPageRoute.
- Simple à utiliser pour des écrans ponctuels
- Pas besoin de configuration préalable
- Idéal pour des écrans qui ne sont utilisés qu'une seule fois
📝 Syntaxe
Voici la syntaxe pour créer une route anonyme :
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MonEcran(),
),
);
🧪 Exemple : Route anonyme avec données
Voici un exemple où on passe des données à l'écran suivant :
chapitre4.2.3-navigator-Routes-anonymes.png
import 'package:flutter/material.dart';
void main() {
runApp(const MonApp());
}
class MonApp extends StatelessWidget {
const MonApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const EcranListe(),
);
}
}
// Écran avec une liste
class EcranListe extends StatelessWidget {
const EcranListe({super.key});
@override
Widget build(BuildContext context) {
final produits = ['Produit A', 'Produit B', 'Produit C'];
return Scaffold(
appBar: AppBar(
title: const Text('Liste des produits'),
),
body: ListView.builder(
itemCount: produits.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(produits[index]),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EcranDetailProduit(
nomProduit: produits[index],
),
),
);
},
);
},
),
);
}
}
// Écran de détail (route anonyme)
class EcranDetailProduit extends StatelessWidget {
final String nomProduit;
const EcranDetailProduit({
super.key,
required this.nomProduit,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(nomProduit),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Détails de : $nomProduit',
style: const TextStyle(fontSize: 20),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Retour'),
),
],
),
),
);
}
}
Dans cet exemple :
- L'écran liste affiche une liste de produits
- Quand vous cliquez sur un produit, une route anonyme est créée vers l'écran de détails
- Le nom du produit est passé en paramètre au constructeur de
EcranDetailProduit
Pour passer des données à un écran via une route anonyme, vous passez simplement les paramètres au constructeur du widget, comme dans l'exemple ci-dessus avec
nomProduit.
4.2.4 – Routes nommées
Les routes nommées permettent de définir tous vos écrans à l'avance dans MaterialApp, puis de naviguer vers eux en utilisant leur nom. C'est plus organisé que les routes anonymes pour des applications avec plusieurs écrans.
📋 Qu'est-ce qu'une route nommée ?
Une route nommée est un écran défini avec un nom unique dans la configuration de votre application. Au lieu de créer l'écran à chaque navigation, vous référencez simplement son nom.
- Plus organisé pour des applications complexes
- Facilite la navigation depuis n'importe oĂą
- Permet de définir des routes par défaut
- Meilleure pour le débogage et la maintenance
📝 Syntaxe
Voici comment définir des routes nommées :
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => EcranAccueil(),
'/details': (context) => EcranDetails(),
'/parametres': (context) => EcranParametres(),
},
)
Pour naviguer vers une route nommée :
Navigator.pushNamed(context, '/details');
🧪 Exemple : Routes nommées
Voici un exemple complet avec des routes nommées :
import 'package:flutter/material.dart';
void main() {
runApp(const MonApp());
}
class MonApp extends StatelessWidget {
const MonApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
routes: {
'/': (context) => const EcranAccueil(),
'/details': (context) => const EcranDetails(),
'/parametres': (context) => const EcranParametres(),
},
);
}
}
// Écran d'accueil
class EcranAccueil extends StatelessWidget {
const EcranAccueil({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Accueil'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/details');
},
child: const Text('Voir les détails'),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/parametres');
},
child: const Text('Paramètres'),
),
],
),
),
);
}
}
// Écran de détails
class EcranDetails extends StatelessWidget {
const EcranDetails({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Détails'),
),
body: const Center(
child: Text(
'Vous êtes sur l\'écran de détails',
style: TextStyle(fontSize: 18),
),
),
);
}
}
// Écran de paramètres
class EcranParametres extends StatelessWidget {
const EcranParametres({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Paramètres'),
),
body: const Center(
child: Text(
'Vous êtes sur l\'écran de paramètres',
style: TextStyle(fontSize: 18),
),
),
);
}
}
Dans cet exemple :
- Les routes sont définies dans
MaterialAppavec des noms :'/','/details','/parametres' initialRoute: '/'définit l'écran affiché au démarrage- Pour naviguer, on utilise
Navigator.pushNamed(context, '/details')
Vous pouvez utiliser soit
home pour un seul écran, soit initialRoute avec des routes nommées. Si vous utilisez des routes nommées, utilisez initialRoute.
Le nom de route
'/' est spécial : c'est la route par défaut. Vous devez toujours la définir si vous utilisez initialRoute.