Navigation avec Flutter

Par pkhalil, 12 mars, 2025

Différences entre GoRouter et Navigator 2.0

Aujourd'hui, je vais démontrer comment naviguer entre les pages avec Flutter. Naviguer dans une application Flutter peut se faire de plusieurs manières. Voici les principales différences entre Navigator 2.0 (la solution native de Flutter) et GoRouter (une bibliothèque tierce qui simplifie la navigation) :


Navigator 2.0

  • Intégré à Flutter :
    Il s'agit de la solution de navigation déclarative native, directement supportée par Flutter.

  • Flexibilité et contrôle :
    Offre une gestion granulaire de la pile de pages, permettant un contrôle précis sur les transitions, le deep linking et la gestion de l'historique.

  • Complexité :
    L'implémentation nécessite de travailler avec des concepts comme RouterDelegate et RouteInformationParser, ce qui peut être complexe pour les cas d'utilisation simples ou pour les débutants.

  • Cas d'utilisation :
    Adapté aux applications nécessitant une logique de navigation sur mesure ou des scénarios de navigation très complexes.


GoRouter

  • Bibliothèque tierce :
    Fournit une couche d’abstraction au-dessus de Navigator 2.0 pour simplifier la configuration des routes.

  • Simplicité de configuration :
    Utilise une syntaxe déclarative simplifiée pour définir les routes et gère automatiquement le deep linking et la mise à jour de l'URL, ce qui réduit le code à écrire.

  • Productivité accrue :
    Réduit la complexité du code de navigation, permettant aux développeurs de se concentrer davantage sur les fonctionnalités de leur application.

  • Cas d'utilisation :
    Idéal pour la plupart des applications courantes où une navigation simple et efficace est requise, sans avoir besoin d'un contrôle ultra-précis.


Guide Étape par Étape : Navigation avec Flutter (Navigator 2.0 et GoRouter)

Ce guide vous montre comment mettre en place la navigation dans une application Flutter en utilisant deux approches : Navigator 2.0 (la solution native) et GoRouter (une bibliothèque qui simplifie la navigation). Vous trouverez ci-dessous toutes les étapes pour créer et configurer votre projet.


Étape 1 : Créer un Nouveau Projet Flutter


Étape 2 : Organiser la Structure du Projet

Pour mieux organiser votre code, vous pouvez créer plusieurs fichiers :

  • main.dart : Point d'entrée de l'application et configuration du routeur.
  • screens.dart : Contiendra les widgets HomeScreen et DetailScreen.
  • (Pour Navigator 2.0) app_state.dart : Modèle d’état de navigation.
  • (Pour Navigator 2.0) my_router_delegate.dart : Implémentation personnalisée du RouterDelegate.
  • (Pour Navigator 2.0) my_route_information_parser.dart : Conversion de l’URL en configuration de navigation.

Étape 3 : Implémentation avec Navigator 2.0

  • 3.1 Créer le Modèle d’État

Créez un fichier app_state.dart et ajoutez-y le code suivant pour gérer l’état de navigation :

// app_state.dart
import 'package:flutter/material.dart';

enum AppPage { home, detail }

class AppState extends ChangeNotifier {
  AppPage _selectedPage = AppPage.home;

  AppPage get selectedPage => _selectedPage;

  void goTo(AppPage page) {
    _selectedPage = page;
    notifyListeners();
  }
}
  • 3.2 Créer le RouterDelegate

Créez un fichier my_router_delegate.dart pour définir le comportement de la navigation :

// my_router_delegate.dart
import 'package:flutter/material.dart';
import 'app_state.dart';
import 'screens.dart';

class MyRouterDelegate extends RouterDelegate<AppPage>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppPage> {
  final GlobalKey<NavigatorState> navigatorKey;
  final AppState appState;

  MyRouterDelegate(this.appState)
      : navigatorKey = GlobalKey<NavigatorState>() {
    appState.addListener(notifyListeners);
  }

  @override
  AppPage? get currentConfiguration => appState.selectedPage;

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: navigatorKey,
      pages: [
        MaterialPage(
          child: HomeScreen(
            onNavigate: () => appState.goTo(AppPage.detail),
          ),
        ),
        if (appState.selectedPage == AppPage.detail)
          MaterialPage(
            child: DetailScreen(
              onBack: () => appState.goTo(AppPage.home),
            ),
          ),
      ],
      onPopPage: (route, result) {
        if (!route.didPop(result)) return false;
        appState.goTo(AppPage.home);
        return true;
      },
    );
  }

  @override
  Future<void> setNewRoutePath(AppPage configuration) async {
    appState.goTo(configuration);
  }
}
  • 3.3 Créer le RouteInformationParser

Créez un fichier my_route_information_parser.dart pour convertir les URL en état de navigation :

// my_route_information_parser.dart
import 'package:flutter/material.dart';
import 'app_state.dart';

class MyRouteInformationParser extends RouteInformationParser<AppPage> {
  @override
  Future<AppPage> parseRouteInformation(
      RouteInformation routeInformation) async {
    final uri = Uri.parse(routeInformation.location ?? '');
    if (uri.pathSegments.contains('detail')) {
      return AppPage.detail;
    }
    return AppPage.home;
  }

  @override
  RouteInformation? restoreRouteInformation(AppPage configuration) {
    switch (configuration) {
      case AppPage.detail:
        return const RouteInformation(location: '/detail');
      case AppPage.home:
      default:
        return const RouteInformation(location: '/');
    }
  }
}
  • 3.4 Créer les Écrans

Créez un fichier screens.dart et ajoutez les widgets pour vos deux écrans :

// screens.dart
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  final VoidCallback onNavigate;
  const HomeScreen({Key? key, required this.onNavigate}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Accueil')),
      body: Center(
        child: ElevatedButton(
          onPressed: onNavigate,
          child: const Text('Aller au détail'),
        ),
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  final VoidCallback onBack;
  const DetailScreen({Key? key, required this.onBack}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Détail'),
        leading: BackButton(onPressed: onBack),
      ),
      body: const Center(child: Text('Page de détail')),
    );
  }
}
  • 3.5 Configurer main.dart pour Navigator 2.0

Modifiez votre main.dart afin d’utiliser Navigator 2.0 :

// main.dart
import 'package:flutter/material.dart';
import 'app_state.dart';
import 'my_router_delegate.dart';
import 'my_route_information_parser.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  
  @override
  Widget build(BuildContext context) {
    final appState = AppState();
    return MaterialApp.router(
      title: 'Demo Navigator 2.0',
      routerDelegate: MyRouterDelegate(appState),
      routeInformationParser: MyRouteInformationParser(),
    );
  }
}

Étape 4 : Implémentation avec GoRouter

Si vous préférez une configuration plus simple, vous pouvez utiliser GoRouter. Pour cela, modifiez (ou créez) le fichier main.dart de la manière suivante :

  1. Ajouter la dépendance GoRouter Dans votre pubspec.yaml, ajoutez :
dependencies:
  flutter:
    sdk: flutter
  go_router: ^7.0.1
  1. Configurer GoRouter dans main.dart :
// main.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'screens.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  
  @override
  Widget build(BuildContext context) {
    final GoRouter _router = GoRouter(
      routes: [
        GoRoute(
          path: '/',
          builder: (context, state) => HomeScreen(
            onNavigate: () => context.go('/detail'),
          ),
        ),
        GoRoute(
          path: '/detail',
          builder: (context, state) => DetailScreen(
            onBack: () => context.pop(),
          ),
        ),
      ],
    );

    return MaterialApp.router(
      title: 'Demo GoRouter',
      routerConfig: _router,
    );
  }
}

Étape 5 : Tester l'Application

Conclusion

  • Navigator 2.0 est parfait si vous avez besoin d'un contrôle fin sur la navigation et que vous êtes à l'aise avec une configuration plus complexe.
  • GoRouter est recommandé pour ceux qui souhaitent une solution rapide, simple et efficace pour gérer la navigation, tout en bénéficiant des avantages du deep linking et d'une gestion de routes facilitée.

En résumé, choisissez Navigator 2.0 pour la flexibilité maximale et GoRouter pour la simplicité et la rapidité de mise en œuvre.


Références

Commentaires2

lcirpaci

il y a 1 mois

Super article, j'ai bien appris aujourd'hui

Est-ce possible de définir des routes dynamiques dans Flutter, où le nom de la route pourrait être déterminé par une donnée en temps réel (par exemple, un ID ou un paramètre spécifique) plutôt que d’être codé en dur ?

pkhalil

il y a 1 mois

En réponse à par lcirpaci

Oui, c'est possible, il y a un widget qui s'appelle MaterialApp contient une fonction qui s'appelle onGenerateRoute. Cette fonction permet d’intercepter la navigation et de créer la page à afficher en fonction du nom de la route reçu.