Widgets

Navigation Hub

Einleitung

Navigation Hubs sind ein zentraler Ort, an dem Sie die Navigation fuer all Ihre Widgets verwalten koennen. Standardmaessig koennen Sie in Sekundenschnelle untere, obere und Journey-Navigations-Layouts erstellen.

Stellen wir uns vor, Sie haben eine App und moechten eine untere Navigationsleiste hinzufuegen, die es den Benutzern ermoeglicht, zwischen verschiedenen Tabs in Ihrer App zu navigieren.

Sie koennen einen Navigation Hub verwenden, um dies umzusetzen.

Schauen wir uns an, wie Sie einen Navigation Hub in Ihrer App verwenden koennen.

Grundlegende Verwendung

Sie koennen einen Navigation Hub mit dem folgenden Befehl erstellen.

metro make:navigation_hub base

Der Befehl fuehrt Sie durch ein interaktives Setup:

  1. Layout-Typ waehlen - Waehlen Sie zwischen navigation_tabs (untere Navigation) oder journey_states (sequenzieller Ablauf).
  2. Tab-/State-Namen eingeben - Geben Sie kommagetrennte Namen fuer Ihre Tabs oder Journey-States an.

Dadurch werden Dateien unter Ihrem resources/pages/navigation_hubs/base/-Verzeichnis erstellt:

  • base_navigation_hub.dart - Das Haupt-Hub-Widget
  • tabs/ oder states/ - Enthaelt die untergeordneten Widgets fuer jeden Tab oder Journey-State

So sieht ein generierter Navigation Hub aus:

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);
  }
}

Sie sehen, dass der Navigation Hub zwei Tabs hat: Home und Settings.

Die layout-Methode gibt den Layout-Typ fuer den Hub zurueck. Sie erhaelt einen BuildContext, sodass Sie beim Konfigurieren Ihres Layouts auf Theme-Daten und Media Queries zugreifen koennen.

Sie koennen weitere Tabs erstellen, indem Sie NavigationTabs zum Navigation Hub hinzufuegen.

Zuerst muessen Sie ein neues Widget mit Metro erstellen.

metro make:stateful_widget news_tab

Sie koennen auch mehrere Widgets gleichzeitig erstellen.

metro make:stateful_widget news_tab,notifications_tab

Dann koennen Sie das neue Widget zum Navigation Hub hinzufuegen.

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

Um den Navigation Hub zu verwenden, fuegen Sie ihn als initiale Route zu Ihrem Router hinzu:

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);

Es gibt noch viel mehr, was Sie mit einem Navigation Hub machen koennen. Schauen wir uns einige der Funktionen an.

Untere Navigation

Sie koennen das Layout auf eine untere Navigationsleiste setzen, indem Sie NavigationHubLayout.bottomNav aus der layout-Methode zurueckgeben.

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

Sie koennen die untere Navigationsleiste anpassen, indem Sie Eigenschaften wie folgt setzen:

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,
    );

Sie koennen einen vordefinierten Stil auf Ihre untere Navigationsleiste anwenden, indem Sie den Parameter style verwenden.

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

Benutzerdefinierter Nav-Bar-Builder

Fuer die vollstaendige Kontrolle ueber Ihre Navigationsleiste koennen Sie den Parameter navBarBuilder verwenden.

Dies ermoeglicht es Ihnen, ein beliebiges benutzerdefiniertes Widget zu erstellen und dabei die Navigationsdaten zu erhalten.

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,
            );
        },
    );

Das NavBarData-Objekt enthaelt:

Eigenschaft Typ Beschreibung
items List<BottomNavigationBarItem> Die Navigationsleisten-Elemente
currentIndex int Der aktuell ausgewaehlte Index
onTap ValueChanged<int> Callback wenn ein Tab angetippt wird

Hier ist ein Beispiel fuer eine vollstaendig benutzerdefinierte Glass-Navigationsleiste:

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,
                        ),
                    ),
                ),
            ),
        );
    },
)

Hinweis: Bei Verwendung von navBarBuilder wird der Parameter style ignoriert.

Obere Navigation

Sie koennen das Layout auf eine obere Navigationsleiste aendern, indem Sie NavigationHubLayout.topNav aus der layout-Methode zurueckgeben.

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

Sie koennen die obere Navigationsleiste anpassen, indem Sie Eigenschaften wie folgt setzen:

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,
    );

Journey-Navigation

Sie koennen das Layout auf eine Journey-Navigation aendern, indem Sie NavigationHubLayout.journey aus der layout-Methode zurueckgeben.

Dies eignet sich hervorragend fuer Onboarding-Flows oder mehrstufige Formulare.

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

Sie koennen auch einen backgroundGradient fuer das Journey-Layout setzen:

@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(),
    ),
);

Hinweis: Wenn backgroundGradient gesetzt ist, hat es Vorrang vor backgroundColor.

Wenn Sie das Journey-Navigations-Layout verwenden moechten, sollten Ihre Widgets JourneyState verwenden, da es viele Hilfsmethoden zur Verwaltung der Journey enthaelt.

Sie koennen die gesamte Journey mit dem Befehl make:navigation_hub und dem Layout journey_states erstellen:

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

Dadurch werden der Hub und alle Journey-State-Widgets unter resources/pages/navigation_hubs/onboarding/states/ erstellt.

Alternativ koennen Sie einzelne Journey-Widgets erstellen mit:

metro make:journey_widget welcome,phone_number_step,add_photos_step

Anschliessend koennen Sie die neuen Widgets zum Navigation Hub hinzufuegen.

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

Journey-Fortschrittsstile

Sie koennen den Fortschrittsanzeige-Stil mit der Klasse JourneyProgressStyle anpassen.

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,
            ),
        ),
    );

Sie koennen die folgenden Fortschrittsindikatoren verwenden:

  • JourneyProgressIndicator.none(): Rendert nichts -- nuetzlich, um den Indikator bei einem bestimmten Tab auszublenden.
  • JourneyProgressIndicator.linear(): Linearer Fortschrittsbalken.
  • JourneyProgressIndicator.dots(): Punkte-basierter Fortschrittsindikator.
  • JourneyProgressIndicator.numbered(): Nummerierter Schritt-Fortschrittsindikator.
  • JourneyProgressIndicator.segments(): Segmentierter Fortschrittsbalken-Stil.
  • JourneyProgressIndicator.circular(): Kreisfoermiger Fortschrittsindikator.
  • JourneyProgressIndicator.timeline(): Timeline-artiger Fortschrittsindikator.
  • JourneyProgressIndicator.custom(): Benutzerdefinierter Fortschrittsindikator mit einer Builder-Funktion.
@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,
                );
            },
        ),
    ),
);

Sie koennen die Position und den Abstand des Fortschrittsindikators innerhalb des JourneyProgressStyle anpassen:

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

Sie koennen die folgenden Positionen fuer den Fortschrittsindikator verwenden:

  • ProgressIndicatorPosition.top: Fortschrittsindikator oben auf dem Bildschirm.
  • ProgressIndicatorPosition.bottom: Fortschrittsindikator unten auf dem Bildschirm.

Pro-Tab Fortschrittsstil-Ueberschreibung

Sie koennen den progressStyle auf Layout-Ebene fuer einzelne Tabs ueberschreiben, indem Sie NavigationTab.journey(progressStyle: ...) verwenden. Tabs ohne eigenen progressStyle erben den Layout-Standard. Tabs ohne Layout-Standard und ohne eigenen Stil zeigen keinen Fortschrittsindikator an.

_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

Die JourneyState-Klasse erweitert NyState um Journey-spezifische Funktionalitaet und erleichtert die Erstellung von Onboarding-Flows und mehrstufigen Journeys.

Um einen neuen JourneyState zu erstellen, koennen Sie den folgenden Befehl verwenden.

metro make:journey_widget onboard_user_dob

Oder wenn Sie mehrere Widgets gleichzeitig erstellen moechten, koennen Sie den folgenden Befehl verwenden.

metro make:journey_widget welcome,phone_number_step,add_photos_step

So sieht ein generiertes JourneyState-Widget aus:

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 {}
}

Sie werden bemerken, dass die JourneyState-Klasse nextStep verwendet, um vorwaerts zu navigieren, und onBackPressed, um zurueckzugehen.

Die nextStep-Methode durchlaeuft den vollstaendigen Validierungs-Lebenszyklus: canContinue() -> onBeforeNext() -> Navigation (oder onComplete() beim letzten Schritt) -> onAfterNext().

Sie koennen auch buildJourneyContent verwenden, um ein strukturiertes Layout mit optionalen Navigations-Buttons zu erstellen:

@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,
      ),
    );
}

Hier sind die Eigenschaften, die Sie in der buildJourneyContent-Methode verwenden koennen.

Eigenschaft Typ Beschreibung
content Widget Der Hauptinhalt der Seite.
nextButton Widget? Das Weiter-Button-Widget.
backButton Widget? Das Zurueck-Button-Widget.
contentPadding EdgeInsetsGeometry Der Abstand fuer den Inhalt.
header Widget? Das Header-Widget.
footer Widget? Das Footer-Widget.
crossAxisAlignment CrossAxisAlignment Die Querachsen-Ausrichtung des Inhalts.

JourneyState-Hilfsmethoden

Die JourneyState-Klasse verfuegt ueber Hilfsmethoden und Eigenschaften, mit denen Sie das Verhalten Ihrer Journey anpassen koennen.

Methode / Eigenschaft Beschreibung
nextStep() Navigiert zum naechsten Schritt mit Validierung. Gibt Future<bool> zurueck.
previousStep() Navigiert zum vorherigen Schritt. Gibt Future<bool> zurueck.
onBackPressed() Einfacher Helfer zum Navigieren zum vorherigen Schritt.
onComplete() Wird aufgerufen, wenn die Journey abgeschlossen ist (am letzten Schritt).
onBeforeNext() Wird aufgerufen, bevor zum naechsten Schritt navigiert wird.
onAfterNext() Wird aufgerufen, nachdem zum naechsten Schritt navigiert wurde.
canContinue() Validierungspruefung vor dem Navigieren zum naechsten Schritt.
isFirstStep Gibt true zurueck, wenn dies der erste Schritt in der Journey ist.
isLastStep Gibt true zurueck, wenn dies der letzte Schritt in der Journey ist.
currentStep Gibt den aktuellen Schritt-Index zurueck (0-basiert).
totalSteps Gibt die Gesamtanzahl der Schritte zurueck.
completionPercentage Gibt den Fortschritt in Prozent zurueck (0.0 bis 1.0).
goToStep(int index) Springt zu einem bestimmten Schritt anhand des Index.
goToNextStep() Springt zum naechsten Schritt (ohne Validierung).
goToPreviousStep() Springt zum vorherigen Schritt (ohne Validierung).
goToFirstStep() Springt zum ersten Schritt.
goToLastStep() Springt zum letzten Schritt.
exitJourney() Verlaesst die Journey durch Pop des Root-Navigators.
resetCurrentStep() Setzt den State des aktuellen Schritts zurueck.
onJourneyComplete Callback wenn die Journey abgeschlossen wird (im letzten Schritt ueberschreiben).
buildJourneyPage() Erstellt eine Vollbild-Journey-Seite mit Scaffold.

nextStep

Die nextStep-Methode navigiert mit vollstaendiger Validierung zum naechsten Schritt. Sie durchlaeuft den Lebenszyklus: canContinue() -> onBeforeNext() -> Navigation oder onComplete() -> onAfterNext().

Sie koennen force: true uebergeben, um die Validierung zu umgehen und direkt zu navigieren.

@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
        ),
    );
}

Um die Validierung zu ueberspringen:

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

previousStep

Die previousStep-Methode navigiert zum vorherigen Schritt. Gibt true bei Erfolg zurueck, false wenn bereits beim ersten Schritt.

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

onBackPressed

Die onBackPressed-Methode ist ein einfacher Helfer, der intern previousStep() aufruft.

@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

Die onComplete-Methode wird aufgerufen, wenn nextStep() beim letzten Schritt ausgeloest wird (nachdem die Validierung bestanden wurde).

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

onBeforeNext

Die onBeforeNext-Methode wird aufgerufen, bevor zum naechsten Schritt navigiert wird.

Z.B. wenn Sie Daten speichern moechten, bevor zum naechsten Schritt navigiert wird, koennen Sie dies hier tun.

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

onAfterNext

Die onAfterNext-Methode wird aufgerufen, nachdem zum naechsten Schritt navigiert wurde.

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

canContinue

Die canContinue-Methode wird aufgerufen, wenn nextStep() ausgeloest wird. Geben Sie false zurueck, um die Navigation zu verhindern.

@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

Die isFirstStep-Eigenschaft gibt true zurueck, wenn dies der erste Schritt in der Journey ist.

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

isLastStep

Die isLastStep-Eigenschaft gibt true zurueck, wenn dies der letzte Schritt in der Journey ist.

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

currentStep

Die currentStep-Eigenschaft gibt den aktuellen Schritt-Index zurueck (0-basiert).

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

totalSteps

Die totalSteps-Eigenschaft gibt die Gesamtanzahl der Schritte in der Journey zurueck.

completionPercentage

Die completionPercentage-Eigenschaft gibt den Fortschritt als Wert von 0.0 bis 1.0 zurueck.

LinearProgressIndicator(value: completionPercentage),

goToStep

Die goToStep-Methode springt direkt zu einem bestimmten Schritt anhand des Index. Dies loest keine Validierung aus.

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

goToNextStep

Die goToNextStep-Methode springt ohne Validierung zum naechsten Schritt. Wenn bereits beim letzten Schritt, passiert nichts.

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

goToPreviousStep

Die goToPreviousStep-Methode springt ohne Validierung zum vorherigen Schritt. Wenn bereits beim ersten Schritt, passiert nichts.

onPressed: () {
    goToPreviousStep();
},

goToFirstStep

Die goToFirstStep-Methode springt zum ersten Schritt.

onPressed: () {
    goToFirstStep();
},

goToLastStep

Die goToLastStep-Methode springt zum letzten Schritt.

onPressed: () {
    goToLastStep();
},

exitJourney

Die exitJourney-Methode verlaesst die Journey durch Pop des Root-Navigators.

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

resetCurrentStep

Die resetCurrentStep-Methode setzt den State des aktuellen Schritts zurueck.

onPressed: () {
    resetCurrentStep();
},

onJourneyComplete

Der onJourneyComplete-Getter kann im letzten Schritt Ihrer Journey ueberschrieben werden, um festzulegen, was passiert, wenn der Benutzer den Flow abschliesst.

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

Die buildJourneyPage-Methode erstellt eine Vollbild-Journey-Seite, die in ein Scaffold mit SafeArea eingebettet ist.

@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,
    );
}
Eigenschaft Typ Beschreibung
content Widget Der Hauptinhalt der Seite.
nextButton Widget? Das Weiter-Button-Widget.
backButton Widget? Das Zurueck-Button-Widget.
contentPadding EdgeInsetsGeometry Der Abstand fuer den Inhalt.
header Widget? Das Header-Widget.
footer Widget? Das Footer-Widget.
backgroundColor Color? Die Hintergrundfarbe des Scaffold.
appBar Widget? Ein optionales AppBar-Widget.
crossAxisAlignment CrossAxisAlignment Die Querachsen-Ausrichtung des Inhalts.

Innerhalb eines Tabs zu Widgets navigieren

Sie koennen innerhalb eines Tabs zu Widgets navigieren, indem Sie den pushTo-Helfer verwenden.

Innerhalb Ihres Tabs koennen Sie den pushTo-Helfer verwenden, um zu einem anderen Widget zu navigieren.

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

Sie koennen auch Daten an das Widget uebergeben, zu dem Sie navigieren.

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

Tabs

Tabs sind die Hauptbausteine eines Navigation Hub.

Sie koennen Tabs zu einem Navigation Hub hinzufuegen, indem Sie die NavigationTab-Klasse und ihre benannten Konstruktoren verwenden.

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),
        ),
    });

Im obigen Beispiel haben wir zwei Tabs zum Navigation Hub hinzugefuegt: Home und Settings.

Sie koennen verschiedene Arten von Tabs verwenden:

  • NavigationTab.tab() - Ein Standard-Navigations-Tab.
  • NavigationTab.badge() - Ein Tab mit Badge-Zaehler.
  • NavigationTab.alert() - Ein Tab mit Alert-Indikator.
  • NavigationTab.journey() - Ein Tab fuer Journey-Navigations-Layouts.

Badges zu Tabs hinzufuegen

Wir haben es einfach gemacht, Badges zu Ihren Tabs hinzuzufuegen.

Badges sind eine grossartige Moeglichkeit, Benutzern zu zeigen, dass es etwas Neues in einem Tab gibt.

Wenn Sie beispielsweise eine Chat-App haben, koennen Sie die Anzahl ungelesener Nachrichten im Chat-Tab anzeigen.

Um ein Badge zu einem Tab hinzuzufuegen, koennen Sie den Konstruktor NavigationTab.badge verwenden.

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),
        ),
    });

Im obigen Beispiel haben wir ein Badge zum Chat-Tab mit einer anfaenglichen Anzahl von 10 hinzugefuegt.

Sie koennen die Badge-Anzahl auch programmatisch aktualisieren.

/// 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);

Standardmaessig wird die Badge-Anzahl gespeichert. Wenn Sie die Badge-Anzahl bei jeder Sitzung zuruecksetzen moechten, koennen Sie rememberCount auf false setzen.

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

Alerts zu Tabs hinzufuegen

Sie koennen Alerts zu Ihren Tabs hinzufuegen.

Manchmal moechten Sie keine Badge-Anzahl anzeigen, sondern dem Benutzer lediglich einen Alert-Indikator anzeigen.

Um einen Alert zu einem Tab hinzuzufuegen, koennen Sie den Konstruktor NavigationTab.alert verwenden.

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),
        ),
    });

Dies fuegt dem Chat-Tab einen Alert mit roter Farbe hinzu.

Sie koennen den Alert auch programmatisch aktualisieren.

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

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

Initialer Index

Standardmaessig startet der Navigation Hub beim ersten Tab (Index 0). Sie koennen dies aendern, indem Sie den initialIndex-Getter ueberschreiben.

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

State beibehalten

Standardmaessig wird der State des Navigation Hub beibehalten.

Das bedeutet, dass wenn Sie zu einem Tab navigieren, der State des Tabs erhalten bleibt.

Wenn Sie den State des Tabs bei jedem Navigieren zuruecksetzen moechten, koennen Sie maintainState auf false setzen.

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

onTap

Sie koennen die onTap-Methode ueberschreiben, um benutzerdefinierte Logik hinzuzufuegen, wenn ein Tab angetippt wird.

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
    }
}

State-Aktionen

State-Aktionen sind eine Moeglichkeit, von ueberall in Ihrer App mit dem Navigation Hub zu interagieren.

Hier sind die State-Aktionen, die Sie verwenden koennen:

/// 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();

Um eine State-Aktion zu verwenden, koennen Sie Folgendes tun:

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

Ladestil

Standardmaessig zeigt der Navigation Hub Ihr Standard-Lade-Widget (resources/widgets/loader_widget.dart) an, wenn der Tab geladen wird.

Sie koennen den loadingStyle anpassen, um den Ladestil zu aendern.

Stil Beschreibung
normal Standard-Ladestil
skeletonizer Skeleton-Ladestil
none Kein Ladestil

Sie koennen den Ladestil wie folgt aendern:

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

Wenn Sie das Lade-Widget in einem der Stile aendern moechten, koennen Sie ein child an den LoadingStyle uebergeben.

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

Wenn der Tab geladen wird, wird nun der Text "Loading..." angezeigt.

Beispiel:

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..."),
        ),
    );
    ...
}

Einen Navigation Hub erstellen

Um einen Navigation Hub zu erstellen, koennen Sie Metro verwenden. Nutzen Sie den folgenden Befehl.

metro make:navigation_hub base

Der Befehl fuehrt Sie durch ein interaktives Setup, bei dem Sie den Layout-Typ waehlen und Ihre Tabs oder Journey-States definieren koennen.

Dadurch wird eine base_navigation_hub.dart-Datei in Ihrem resources/pages/navigation_hubs/base/-Verzeichnis erstellt, mit untergeordneten Widgets in den Unterordnern tabs/ oder states/.