Widgets

Navigation Hub

Introduction

Les Navigation Hubs sont un endroit central où vous pouvez gérer la navigation de tous vos widgets. Par défaut, vous pouvez créer des mises en page de navigation inférieure, supérieure et journey en quelques secondes.

Imaginons que vous avez une application et que vous souhaitez ajouter une barre de navigation inférieure permettant aux utilisateurs de naviguer entre différents onglets.

Vous pouvez utiliser un Navigation Hub pour construire cela.

Voyons comment utiliser un Navigation Hub dans votre application.

Utilisation de base

Vous pouvez créer un Navigation Hub en utilisant la commande ci-dessous.

metro make:navigation_hub base

La commande vous guidera à travers une configuration interactive :

  1. Choisir un type de mise en page - Sélectionnez entre navigation_tabs (navigation inférieure) ou journey_states (flux séquentiel).
  2. Saisir les noms des tabs/states - Fournissez des noms séparés par des virgules pour vos tabs ou journey states.

Cela créera des fichiers dans votre répertoire resources/pages/navigation_hubs/base/ :

  • base_navigation_hub.dart - Le widget principal du hub
  • tabs/ ou states/ - Contient les widgets enfants pour chaque tab ou journey state

Voici à quoi ressemble un Navigation Hub généré :

import 'package:flutter/material.dart';
import 'package:nylo_framework/nylo_framework.dart';
import '/resources/pages/navigation_hubs/base/tabs/home_tab_widget.dart';
import '/resources/pages/navigation_hubs/base/tabs/settings_tab_widget.dart';

class BaseNavigationHub extends NyStatefulWidget with BottomNavPageControls {
  static RouteView path = ("/base", (_) => BaseNavigationHub());

  BaseNavigationHub()
      : super(
            child: () => _BaseNavigationHubState(),
            stateName: path.stateName());

  /// State actions
  static NavigationHubStateActions stateActions = NavigationHubStateActions(path.stateName());
}

class _BaseNavigationHubState extends NavigationHub<BaseNavigationHub> {

  /// Layout builder
  @override
  NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav();

  /// Should the state be maintained
  @override
  bool get maintainState => true;

  /// The initial index
  @override
  int get initialIndex => 0;

  /// Navigation pages
  _BaseNavigationHubState() : super(() => {
      0: NavigationTab.tab(title: "Home", page: HomeTab()),
      1: NavigationTab.tab(title: "Settings", page: SettingsTab()),
  });

  /// Handle the tap event
  @override
  onTap(int index) {
    super.onTap(index);
  }
}

Vous pouvez voir que le Navigation Hub possède deux onglets, Home et Settings.

La méthode layout retourne le type de mise en page du hub. Elle reçoit un BuildContext qui vous permet d'accéder aux données du thème et aux media queries lors de la configuration de votre mise en page.

Vous pouvez créer davantage d'onglets en ajoutant des NavigationTab au Navigation Hub.

Tout d'abord, vous devez créer un nouveau widget en utilisant Metro.

metro make:stateful_widget news_tab

Vous pouvez également créer plusieurs widgets en une seule fois.

metro make:stateful_widget news_tab,notifications_tab

Ensuite, vous pouvez ajouter le nouveau widget au Navigation Hub.

_BaseNavigationHubState() : super(() => {
    0: NavigationTab.tab(title: "Home", page: HomeTab()),
    1: NavigationTab.tab(title: "Settings", page: SettingsTab()),
    2: NavigationTab.tab(title: "News", page: NewsTab()),
});

Pour utiliser le Navigation Hub, ajoutez-le à votre routeur comme route initiale :

import 'package:nylo_framework/nylo_framework.dart';

appRouter() => nyRoutes((router) {
    ...
    router.add(BaseNavigationHub.path).initialRoute();
});

// or navigate to the Navigation Hub from anywhere in your app

routeTo(BaseNavigationHub.path);

Il y a beaucoup plus de choses que vous pouvez faire avec un Navigation Hub. Explorons certaines de ses fonctionnalités.

Navigation inférieure

Vous pouvez définir la mise en page sur une barre de navigation inférieure en retournant NavigationHubLayout.bottomNav depuis la méthode layout.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav();

Vous pouvez personnaliser la barre de navigation inférieure en définissant des propriétés comme celles-ci :

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav(
        backgroundColor: Colors.white,
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.grey,
        elevation: 8.0,
        iconSize: 24.0,
        selectedFontSize: 14.0,
        unselectedFontSize: 12.0,
        showSelectedLabels: true,
        showUnselectedLabels: true,
        type: BottomNavigationBarType.fixed,
    );

Vous pouvez appliquer un style prédéfini à votre barre de navigation inférieure en utilisant le paramètre style.

@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav(
    style: BottomNavStyle.material(), // Default Flutter material style
);

Constructeur de barre de navigation personnalisé

Pour un contrôle total sur votre barre de navigation, vous pouvez utiliser le paramètre navBarBuilder.

Cela vous permet de construire n'importe quel widget personnalisé tout en recevant les données de navigation.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav(
        navBarBuilder: (context, data) {
            return MyCustomNavBar(
                items: data.items,
                currentIndex: data.currentIndex,
                onTap: data.onTap,
            );
        },
    );

L'objet NavBarData contient :

Propriété Type Description
items List<BottomNavigationBarItem> Les éléments de la barre de navigation
currentIndex int L'index actuellement sélectionné
onTap ValueChanged<int> Callback lorsqu'un onglet est touché

Voici un exemple de barre de navigation glass entièrement personnalisée :

NavigationHubLayout.bottomNav(
    navBarBuilder: (context, data) {
        return Padding(
            padding: EdgeInsets.all(16),
            child: ClipRRect(
                borderRadius: BorderRadius.circular(25),
                child: BackdropFilter(
                    filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
                    child: Container(
                        decoration: BoxDecoration(
                            color: Colors.white.withValues(alpha: 0.7),
                            borderRadius: BorderRadius.circular(25),
                        ),
                        child: BottomNavigationBar(
                            items: data.items,
                            currentIndex: data.currentIndex,
                            onTap: data.onTap,
                            backgroundColor: Colors.transparent,
                            elevation: 0,
                        ),
                    ),
                ),
            ),
        );
    },
)

Note : Lorsque vous utilisez navBarBuilder, le paramètre style est ignoré.

Navigation supérieure

Vous pouvez changer la mise en page pour une barre de navigation supérieure en retournant NavigationHubLayout.topNav depuis la méthode layout.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.topNav();

Vous pouvez personnaliser la barre de navigation supérieure en définissant des propriétés comme celles-ci :

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.topNav(
        backgroundColor: Colors.white,
        labelColor: Colors.blue,
        unselectedLabelColor: Colors.grey,
        indicatorColor: Colors.blue,
        indicatorWeight: 3.0,
        isScrollable: false,
        hideAppBarTitle: true,
    );

Navigation journey

Vous pouvez changer la mise en page pour une navigation journey en retournant NavigationHubLayout.journey depuis la méthode layout.

C'est idéal pour les flux d'onboarding ou les formulaires multi-étapes.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.journey(
        progressStyle: JourneyProgressStyle(
          indicator: JourneyProgressIndicator.segments(),
        ),
    );

Vous pouvez également définir un backgroundGradient pour la mise en page journey :

@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.journey(
    backgroundGradient: LinearGradient(
        colors: [Colors.blue, Colors.purple],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
    ),
    progressStyle: JourneyProgressStyle(
      indicator: JourneyProgressIndicator.linear(),
    ),
);

Note : Lorsque backgroundGradient est défini, il a priorité sur backgroundColor.

Si vous souhaitez utiliser la mise en page de navigation journey, vos widgets devraient utiliser JourneyState car il contient de nombreuses méthodes d'aide pour gérer le parcours.

Vous pouvez créer l'ensemble du journey en utilisant la commande make:navigation_hub avec la mise en page journey_states :

metro make:navigation_hub onboarding
# Select: journey_states
# Enter: welcome, personal_info, add_photos

Cela créera le hub ainsi que tous les widgets de journey state dans resources/pages/navigation_hubs/onboarding/states/.

Vous pouvez également créer des widgets journey individuels en utilisant :

metro make:journey_widget welcome,phone_number_step,add_photos_step

Vous pouvez ensuite ajouter les nouveaux widgets au Navigation Hub.

_MyNavigationHubState() : super(() => {
    0: NavigationTab.journey(
        page: Welcome(),
    ),
    1: NavigationTab.journey(
        page: PhoneNumberStep(),
    ),
    2: NavigationTab.journey(
        page: AddPhotosStep(),
    ),
});

Styles de progression journey

Vous pouvez personnaliser le style de l'indicateur de progression en utilisant la classe JourneyProgressStyle.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.journey(
        progressStyle: JourneyProgressStyle(
            indicator: JourneyProgressIndicator.linear(
                activeColor: Colors.blue,
                inactiveColor: Colors.grey,
                thickness: 4.0,
            ),
        ),
    );

Vous pouvez utiliser les indicateurs de progression suivants :

  • JourneyProgressIndicator.none() : Ne rend rien -- utile pour masquer l'indicateur sur un onglet spécifique.
  • JourneyProgressIndicator.linear() : Barre de progression linéaire.
  • JourneyProgressIndicator.dots() : Indicateur de progression à points.
  • JourneyProgressIndicator.numbered() : Indicateur de progression avec étapes numérotées.
  • JourneyProgressIndicator.segments() : Style de barre de progression segmentée.
  • JourneyProgressIndicator.circular() : Indicateur de progression circulaire.
  • JourneyProgressIndicator.timeline() : Indicateur de progression en style timeline.
  • JourneyProgressIndicator.custom() : Indicateur de progression personnalisé utilisant une fonction builder.
@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.journey(
    progressStyle: JourneyProgressStyle(
        indicator: JourneyProgressIndicator.custom(
            builder: (context, currentStep, totalSteps, percentage) {
                return LinearProgressIndicator(
                    value: percentage,
                    backgroundColor: Colors.grey[200],
                    color: Colors.blue,
                    minHeight: 4.0,
                );
            },
        ),
    ),
);

Vous pouvez personnaliser la position et le padding de l'indicateur de progression dans le JourneyProgressStyle :

@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.journey(
    progressStyle: JourneyProgressStyle(
        indicator: JourneyProgressIndicator.dots(),
        position: ProgressIndicatorPosition.bottom,
        padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
    ),
);

Vous pouvez utiliser les positions d'indicateur de progression suivantes :

  • ProgressIndicatorPosition.top : Indicateur de progression en haut de l'écran.
  • ProgressIndicatorPosition.bottom : Indicateur de progression en bas de l'écran.

Remplacement du style de progression par onglet

Vous pouvez remplacer le progressStyle au niveau du layout sur des onglets individuels en utilisant NavigationTab.journey(progressStyle: ...). Les onglets sans leur propre progressStyle héritent du style par défaut du layout. Les onglets sans style par défaut du layout et sans style par onglet n'afficheront pas d'indicateur de progression.

_MyNavigationHubState() : super(() => {
    0: NavigationTab.journey(
        page: Welcome(),
    ),
    1: NavigationTab.journey(
        page: PhoneNumberStep(),
        progressStyle: JourneyProgressStyle(
            indicator: JourneyProgressIndicator.numbered(),
        ), // overrides the layout default for this tab only
    ),
    2: NavigationTab.journey(
        page: AddPhotosStep(),
    ),
});

JourneyState

La classe JourneyState étend NyState avec des fonctionnalités spécifiques au journey pour faciliter la création de flux d'onboarding et de parcours multi-étapes.

Pour créer un nouveau JourneyState, vous pouvez utiliser la commande ci-dessous.

metro make:journey_widget onboard_user_dob

Ou si vous souhaitez créer plusieurs widgets en une seule fois, vous pouvez utiliser la commande suivante.

metro make:journey_widget welcome,phone_number_step,add_photos_step

Voici à quoi ressemble un widget JourneyState généré :

import 'package:flutter/material.dart';
import '/resources/pages/navigation_hubs/onboarding/onboarding_navigation_hub.dart';
import '/resources/widgets/buttons/buttons.dart';
import 'package:nylo_framework/nylo_framework.dart';

class Welcome extends StatefulWidget {
  const Welcome({super.key});

  @override
  createState() => _WelcomeState();
}

class _WelcomeState extends JourneyState<Welcome> {
  _WelcomeState() : super(
      navigationHubState: OnboardingNavigationHub.path.stateName());

  @override
  get init => () {
    // Your initialization logic here
  };

  @override
  Widget view(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        children: [
          Expanded(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('Welcome', style: Theme.of(context).textTheme.headlineMedium),
                  const SizedBox(height: 20),
                  Text('This onboarding journey will help you get started.'),
                ],
              ),
            ),
          ),

          // Navigation buttons
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              if (!isFirstStep)
                Flexible(
                  child: Button.textOnly(
                    text: "Back",
                    textColor: Colors.black87,
                    onPressed: onBackPressed,
                  ),
                )
              else
                const SizedBox.shrink(),
              Flexible(
                child: Button.primary(
                  text: "Continue",
                  onPressed: nextStep,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }

  /// Check if the journey can continue to the next step
  @override
  Future<bool> canContinue() async {
    return true;
  }

  /// Called before navigating to the next step
  @override
  Future<void> onBeforeNext() async {
    // E.g. save data to session
  }

  /// Called when the journey is complete (at the last step)
  @override
  Future<void> onComplete() async {}
}

Vous remarquerez que la classe JourneyState utilise nextStep pour avancer et onBackPressed pour revenir en arrière.

La méthode nextStep exécute l'ensemble du cycle de vie de validation : canContinue() -> onBeforeNext() -> navigation (ou onComplete() si c'est la dernière étape) -> onAfterNext().

Vous pouvez également utiliser buildJourneyContent pour construire une mise en page structurée avec des boutons de navigation optionnels :

@override
Widget view(BuildContext context) {
    return buildJourneyContent(
      content: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('Welcome', style: Theme.of(context).textTheme.headlineMedium),
          const SizedBox(height: 20),
          Text('This onboarding journey will help you get started.'),
        ],
      ),
      nextButton: Button.primary(
        text: isLastStep ? "Get Started" : "Continue",
        onPressed: nextStep,
      ),
      backButton: isFirstStep ? null : Button.textOnly(
        text: "Back",
        textColor: Colors.black87,
        onPressed: onBackPressed,
      ),
    );
}

Voici les propriétés que vous pouvez utiliser dans la méthode buildJourneyContent.

Propriété Type Description
content Widget Le contenu principal de la page.
nextButton Widget? Le widget du bouton suivant.
backButton Widget? Le widget du bouton retour.
contentPadding EdgeInsetsGeometry Le padding du contenu.
header Widget? Le widget d'en-tête.
footer Widget? Le widget de pied de page.
crossAxisAlignment CrossAxisAlignment L'alignement de l'axe transversal du contenu.

Méthodes d'aide JourneyState

La classe JourneyState possède des méthodes et propriétés d'aide que vous pouvez utiliser pour personnaliser le comportement de votre parcours.

Méthode / Propriété Description
nextStep() Navigue vers l'étape suivante avec validation. Retourne Future<bool>.
previousStep() Navigue vers l'étape précédente. Retourne Future<bool>.
onBackPressed() Helper simple pour naviguer vers l'étape précédente.
onComplete() Appelée lorsque le parcours est terminé (à la dernière étape).
onBeforeNext() Appelée avant la navigation vers l'étape suivante.
onAfterNext() Appelée après la navigation vers l'étape suivante.
canContinue() Vérification de validation avant la navigation vers l'étape suivante.
isFirstStep Retourne true si c'est la première étape du parcours.
isLastStep Retourne true si c'est la dernière étape du parcours.
currentStep Retourne l'index de l'étape actuelle (base 0).
totalSteps Retourne le nombre total d'étapes.
completionPercentage Retourne le pourcentage de complétion (0.0 à 1.0).
goToStep(int index) Sauter directement à une étape spécifique par index.
goToNextStep() Sauter à l'étape suivante (sans validation).
goToPreviousStep() Sauter à l'étape précédente (sans validation).
goToFirstStep() Sauter à la première étape.
goToLastStep() Sauter à la dernière étape.
exitJourney() Quitter le journey en faisant un pop du navigateur racine.
resetCurrentStep() Réinitialiser l'état de l'étape actuelle.
onJourneyComplete Callback lorsque le journey est terminé (à surcharger dans la dernière étape).
buildJourneyPage() Construire une page journey plein écran avec Scaffold.

nextStep

La méthode nextStep navigue vers l'étape suivante avec une validation complète. Elle exécute le cycle de vie : canContinue() -> onBeforeNext() -> navigation ou onComplete() -> onAfterNext().

Vous pouvez passer force: true pour contourner la validation et naviguer directement.

@override
Widget view(BuildContext context) {
    return buildJourneyContent(
        content: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
                ...
            ],
        ),
        nextButton: Button.primary(
            text: isLastStep ? "Get Started" : "Continue",
            onPressed: nextStep, // runs validation then navigates
        ),
    );
}

Pour ignorer la validation :

onPressed: () => nextStep(force: true),

previousStep

La méthode previousStep navigue vers l'étape précédente. Retourne true en cas de succès, false si vous êtes déjà à la première étape.

onPressed: () async {
    bool success = await previousStep();
    if (!success) {
      // Already at first step
    }
},

onBackPressed

La méthode onBackPressed est un helper simple qui appelle previousStep() en interne.

@override
Widget view(BuildContext context) {
    return buildJourneyContent(
        content: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
                ...
            ],
        ),
        backButton: isFirstStep ? null : Button.textOnly(
            text: "Back",
            textColor: Colors.black87,
            onPressed: onBackPressed,
        ),
    );
}

onComplete

La méthode onComplete est appelée lorsque nextStep() est déclenché à la dernière étape (après que la validation est passée).

@override
Future<void> onComplete() async {
    print("Journey completed");
}

onBeforeNext

La méthode onBeforeNext est appelée avant la navigation vers l'étape suivante.

Par exemple, si vous souhaitez sauvegarder des données avant de naviguer vers l'étape suivante, vous pouvez le faire ici.

@override
Future<void> onBeforeNext() async {
    // E.g. save data to session
    // session('onboarding', {
    //   'name': 'Anthony Gordon',
    //   'occupation': 'Software Engineer',
    // });
}

onAfterNext

La méthode onAfterNext est appelée après la navigation vers l'étape suivante.

@override
Future<void> onAfterNext() async {
    // print('Navigated to the next step');
}

canContinue

La méthode canContinue est appelée lorsque nextStep() est déclenché. Retournez false pour empêcher la navigation.

@override
Future<bool> canContinue() async {
    // Perform your validation logic here
    // Return true if the journey can continue, false otherwise
    if (nameController.text.isEmpty) {
        showToastSorry(description: "Please enter your name");
        return false;
    }
    return true;
}

isFirstStep

La propriété isFirstStep retourne true si c'est la première étape du parcours.

backButton: isFirstStep ? null : Button.textOnly(
    text: "Back",
    textColor: Colors.black87,
    onPressed: onBackPressed,
),

isLastStep

La propriété isLastStep retourne true si c'est la dernière étape du parcours.

nextButton: Button.primary(
    text: isLastStep ? "Get Started" : "Continue",
    onPressed: nextStep,
),

currentStep

La propriété currentStep retourne l'index de l'étape actuelle (base 0).

Text("Step ${currentStep + 1} of $totalSteps"),

totalSteps

La propriété totalSteps retourne le nombre total d'étapes du parcours.

completionPercentage

La propriété completionPercentage retourne le pourcentage de complétion sous forme de valeur allant de 0.0 à 1.0.

LinearProgressIndicator(value: completionPercentage),

goToStep

La méthode goToStep saute directement à une étape spécifique par index. Cela ne déclenche pas de validation.

nextButton: Button.primary(
    text: "Skip to photos",
    onPressed: () {
        goToStep(2); // jump to step index 2
    },
),

goToNextStep

La méthode goToNextStep saute à l'étape suivante sans validation. Si vous êtes déjà à la dernière étape, elle ne fait rien.

onPressed: () {
    goToNextStep(); // skip validation and go to next step
},

goToPreviousStep

La méthode goToPreviousStep saute à l'étape précédente sans validation. Si vous êtes déjà à la première étape, elle ne fait rien.

onPressed: () {
    goToPreviousStep();
},

goToFirstStep

La méthode goToFirstStep saute à la première étape.

onPressed: () {
    goToFirstStep();
},

goToLastStep

La méthode goToLastStep saute à la dernière étape.

onPressed: () {
    goToLastStep();
},

exitJourney

La méthode exitJourney quitte le journey en faisant un pop du navigateur racine.

onPressed: () {
    exitJourney(); // pop the root navigator
},

resetCurrentStep

La méthode resetCurrentStep réinitialise l'état de l'étape actuelle.

onPressed: () {
    resetCurrentStep();
},

onJourneyComplete

Le getter onJourneyComplete peut être surchargé dans la dernière étape de votre journey pour définir ce qui se passe lorsque l'utilisateur termine le flux.

class _CompleteStepState extends JourneyState<CompleteStep> {
  _CompleteStepState() : super(
      navigationHubState: OnboardingNavigationHub.path.stateName());

  /// Callback when journey completes
  @override
  void Function()? get onJourneyComplete => () {
    // Navigate to your home page or next destination
    routeTo(HomePage.path);
  };

  @override
  Widget view(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        children: [
          ...
          Button.primary(
            text: "Get Started",
            onPressed: onJourneyComplete, // triggers the completion callback
          ),
        ],
      ),
    );
  }
}

buildJourneyPage

La méthode buildJourneyPage construit une page journey plein écran enveloppée dans un Scaffold avec SafeArea.

@override
Widget view(BuildContext context) {
    return buildJourneyPage(
      content: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('Welcome', style: Theme.of(context).textTheme.headlineMedium),
        ],
      ),
      nextButton: Button.primary(
        text: "Continue",
        onPressed: nextStep,
      ),
      backgroundColor: Colors.white,
    );
}
Propriété Type Description
content Widget Le contenu principal de la page.
nextButton Widget? Le widget du bouton suivant.
backButton Widget? Le widget du bouton retour.
contentPadding EdgeInsetsGeometry Le padding du contenu.
header Widget? Le widget d'en-tête.
footer Widget? Le widget de pied de page.
backgroundColor Color? La couleur d'arrière-plan du Scaffold.
appBar Widget? Un widget AppBar optionnel.
crossAxisAlignment CrossAxisAlignment L'alignement de l'axe transversal du contenu.

Naviguer vers des widgets au sein d'un onglet

Vous pouvez naviguer vers des widgets au sein d'un onglet en utilisant le helper pushTo.

Dans votre onglet, vous pouvez utiliser le helper pushTo pour naviguer vers un autre widget.

_HomeTabState extends State<HomeTab> {
    ...
    void _navigateToSettings() {
        pushTo(SettingsPage());
    }
    ...
}

Vous pouvez également passer des données au widget vers lequel vous naviguez.

_HomeTabState extends State<HomeTab> {
    ...
    void _navigateToSettings() {
        pushTo(SettingsPage(), data: {"name": "Anthony"});
    }
    ...
}

Onglets

Les onglets sont les éléments de base d'un Navigation Hub.

Vous pouvez ajouter des onglets à un Navigation Hub en utilisant la classe NavigationTab et ses constructeurs nommés.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav();
    ...
    _MyNavigationHubState() : super(() => {
        0: NavigationTab.tab(
            title: "Home",
            page: HomeTab(),
            icon: Icon(Icons.home),
            activeIcon: Icon(Icons.home),
        ),
        1: NavigationTab.tab(
            title: "Settings",
            page: SettingsTab(),
            icon: Icon(Icons.settings),
            activeIcon: Icon(Icons.settings),
        ),
    });

Dans l'exemple ci-dessus, nous avons ajouté deux onglets au Navigation Hub, Home et Settings.

Vous pouvez utiliser différents types d'onglets :

  • NavigationTab.tab() - Un onglet de navigation standard.
  • NavigationTab.badge() - Un onglet avec un compteur de badge.
  • NavigationTab.alert() - Un onglet avec un indicateur d'alerte.
  • NavigationTab.journey() - Un onglet pour les mises en page de navigation journey.

Ajouter des badges aux onglets

Nous avons facilité l'ajout de badges à vos onglets.

Les badges sont un excellent moyen de montrer aux utilisateurs qu'il y a quelque chose de nouveau dans un onglet.

Par exemple, si vous avez une application de chat, vous pouvez afficher le nombre de messages non lus dans l'onglet de chat.

Pour ajouter un badge à un onglet, vous pouvez utiliser le constructeur NavigationTab.badge.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav();
    ...
    _MyNavigationHubState() : super(() => {
        0: NavigationTab.badge(
            title: "Chats",
            page: ChatTab(),
            icon: Icon(Icons.message),
            activeIcon: Icon(Icons.message),
            initialCount: 10,
        ),
        1: NavigationTab.tab(
            title: "Settings",
            page: SettingsTab(),
            icon: Icon(Icons.settings),
            activeIcon: Icon(Icons.settings),
        ),
    });

Dans l'exemple ci-dessus, nous avons ajouté un badge à l'onglet Chat avec un compteur initial de 10.

Vous pouvez également mettre à jour le compteur du badge par programmation.

/// Increment the badge count
BaseNavigationHub.stateActions.incrementBadgeCount(tab: 0);

/// Update the badge count
BaseNavigationHub.stateActions.updateBadgeCount(tab: 0, count: 5);

/// Clear the badge count
BaseNavigationHub.stateActions.clearBadgeCount(tab: 0);

Par défaut, le compteur du badge sera mémorisé. Si vous souhaitez effacer le compteur du badge à chaque session, vous pouvez définir rememberCount à false.

0: NavigationTab.badge(
    title: "Chats",
    page: ChatTab(),
    icon: Icon(Icons.message),
    activeIcon: Icon(Icons.message),
    initialCount: 10,
    rememberCount: false,
),

Ajouter des alertes aux onglets

Vous pouvez ajouter des alertes à vos onglets.

Parfois, vous ne souhaitez pas afficher un compteur de badge, mais vous voulez montrer un indicateur d'alerte à l'utilisateur.

Pour ajouter une alerte à un onglet, vous pouvez utiliser le constructeur NavigationTab.alert.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav();
    ...
    _MyNavigationHubState() : super(() => {
        0: NavigationTab.alert(
            title: "Chats",
            page: ChatTab(),
            icon: Icon(Icons.message),
            activeIcon: Icon(Icons.message),
            alertColor: Colors.red,
            alertEnabled: true,
            rememberAlert: false,
        ),
        1: NavigationTab.tab(
            title: "Settings",
            page: SettingsTab(),
            icon: Icon(Icons.settings),
            activeIcon: Icon(Icons.settings),
        ),
    });

Cela ajoutera une alerte à l'onglet Chat avec une couleur rouge.

Vous pouvez également mettre à jour l'alerte par programmation.

/// Enable the alert
BaseNavigationHub.stateActions.alertEnableTab(tab: 0);

/// Disable the alert
BaseNavigationHub.stateActions.alertDisableTab(tab: 0);

Index initial

Par défaut, le Navigation Hub démarre sur le premier onglet (index 0). Vous pouvez modifier cela en surchargeant le getter initialIndex.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    int get initialIndex => 1; // Start on the second tab
    ...
}

Maintien de l'état

Par défaut, l'état du Navigation Hub est maintenu.

Cela signifie que lorsque vous naviguez vers un onglet, l'état de l'onglet est préservé.

Si vous souhaitez effacer l'état de l'onglet à chaque fois que vous y naviguez, vous pouvez définir maintainState à false.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    bool get maintainState => false;
    ...
}

onTap

Vous pouvez surcharger la méthode onTap pour ajouter une logique personnalisée lorsqu'un onglet est touché.

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    @override
    onTap(int index) {
        // Add custom logic here
        // E.g. track analytics, show confirmation, etc.
        super.onTap(index); // Always call super to handle the tab switch
    }
}

Actions d'état

Les actions d'état sont un moyen d'interagir avec le Navigation Hub depuis n'importe où dans votre application.

Voici les actions d'état que vous pouvez utiliser :

/// Reset the tab at a given index
/// E.g. MyNavigationHub.stateActions.resetTabIndex(0);
resetTabIndex(int tabIndex);

/// Change the current tab programmatically
/// E.g. MyNavigationHub.stateActions.currentTabIndex(2);
currentTabIndex(int tabIndex);

/// Update the badge count
/// E.g. MyNavigationHub.stateActions.updateBadgeCount(tab: 0, count: 2);
updateBadgeCount({required int tab, required int count});

/// Increment the badge count
/// E.g. MyNavigationHub.stateActions.incrementBadgeCount(tab: 0);
incrementBadgeCount({required int tab});

/// Clear the badge count
/// E.g. MyNavigationHub.stateActions.clearBadgeCount(tab: 0);
clearBadgeCount({required int tab});

/// Enable the alert for a tab
/// E.g. MyNavigationHub.stateActions.alertEnableTab(tab: 0);
alertEnableTab({required int tab});

/// Disable the alert for a tab
/// E.g. MyNavigationHub.stateActions.alertDisableTab(tab: 0);
alertDisableTab({required int tab});

/// Navigate to the next page in a journey layout
/// E.g. await MyNavigationHub.stateActions.nextPage();
Future<bool> nextPage();

/// Navigate to the previous page in a journey layout
/// E.g. await MyNavigationHub.stateActions.previousPage();
Future<bool> previousPage();

Pour utiliser une action d'état, vous pouvez faire ce qui suit :

MyNavigationHub.stateActions.updateBadgeCount(tab: 0, count: 2);

MyNavigationHub.stateActions.resetTabIndex(0);

MyNavigationHub.stateActions.currentTabIndex(2); // Switch to tab 2

await MyNavigationHub.stateActions.nextPage(); // Journey: go to next page

Style de chargement

Par défaut, le Navigation Hub affichera votre widget de chargement par défaut (resources/widgets/loader_widget.dart) lorsque l'onglet est en cours de chargement.

Vous pouvez personnaliser le loadingStyle pour modifier le style de chargement.

Style Description
normal Style de chargement par défaut
skeletonizer Style de chargement squelette
none Pas de style de chargement

Vous pouvez changer le style de chargement comme ceci :

@override
LoadingStyle get loadingStyle => LoadingStyle.normal();
// or
@override
LoadingStyle get loadingStyle => LoadingStyle.skeletonizer();

Si vous souhaitez modifier le widget de chargement dans l'un des styles, vous pouvez passer un child au LoadingStyle.

@override
LoadingStyle get loadingStyle => LoadingStyle.normal(
    child: Center(
        child: Text("Loading..."),
    ),
);

Désormais, lorsque l'onglet est en cours de chargement, le texte "Loading..." sera affiché.

Exemple ci-dessous :

class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
    ...
    _MyNavigationHubState() : super(() async {

      await sleep(3); // simulate loading for 3 seconds

      return {
        0: NavigationTab.tab(
          title: "Home",
          page: HomeTab(),
        ),
        1: NavigationTab.tab(
          title: "Settings",
          page: SettingsTab(),
        ),
      };
    });

    @override
    LoadingStyle get loadingStyle => LoadingStyle.normal(
        child: Center(
            child: Text("Loading..."),
        ),
    );
    ...
}

Créer un Navigation Hub

Pour créer un Navigation Hub, vous pouvez utiliser Metro, utilisez la commande ci-dessous.

metro make:navigation_hub base

La commande vous guidera à travers une configuration interactive où vous pourrez choisir le type de mise en page et définir vos tabs ou journey states.

Cela créera un fichier base_navigation_hub.dart dans votre répertoire resources/pages/navigation_hubs/base/ avec les widgets enfants organisés dans les sous-dossiers tabs/ ou states/.