↑
CHAPITRE 4.2

Navigation entre écrans

Naviguez entre les différentes pages de votre application
La navigation est essentielle dans toute application mobile. Dans ce chapitre, nous allons découvrir comment passer d'un écran à un autre, comment gérer la pile de navigation, et comment utiliser les routes nommées pour une navigation plus organisée.

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.

Analogie :
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 :
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);
Important :
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 actuel
  • MaterialPageRoute : 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 :

Exemple Navigator.push et Navigator.pop Flutter
L'utilisation de 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 :
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.
Astuce :
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.
Bouton retour dans l'AppBar :
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Ă© leading de l'AppBar si 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.

Avantages des routes anonymes :
  • 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
Exemple Navigator.push et Navigator.pop Flutter
L'utilisation de routes anonymes 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 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
Passer des données :
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.

Avantages des routes nommées :
  • 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 :

Exemple Navigator.push et Navigator.pop Flutter
L'utilisation de routes nommées pour naviguer entre trois é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,
      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 MaterialApp avec des noms : '/', '/details', '/parametres'
  • initialRoute: '/' dĂ©finit l'Ă©cran affichĂ© au dĂ©marrage
  • Pour naviguer, on utilise Navigator.pushNamed(context, '/details')
initialRoute vs home :
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.
Important :
Le nom de route '/' est spécial : c'est la route par défaut. Vous devez toujours la définir si vous utilisez initialRoute.