Navigation Hub
Introduzione
I Navigation Hub sono un punto centrale in cui puoi gestire la navigazione di tutti i tuoi widget. Fin da subito puoi creare layout di navigazione inferiore, superiore e journey in pochi secondi.
Immaginiamo di avere un'app e di voler aggiungere una barra di navigazione inferiore per permettere agli utenti di spostarsi tra i diversi tab dell'app.
Puoi utilizzare un Navigation Hub per realizzarlo.
Vediamo nel dettaglio come usare un Navigation Hub nella tua app.
Utilizzo Base
Puoi creare un Navigation Hub usando il comando seguente.
metro make:navigation_hub base
Il comando ti guiderà attraverso una configurazione interattiva:
- Scegli un tipo di layout - Seleziona tra
navigation_tabs(navigazione inferiore) ojourney_states(flusso sequenziale). - Inserisci i nomi dei tab/state - Fornisci i nomi dei tuoi tab o journey state separati da virgola.
Questo creerà i file nella directory resources/pages/navigation_hubs/base/:
base_navigation_hub.dart- Il widget hub principaletabs/ostates/- Contiene i widget figli per ogni tab o journey state
Ecco come appare un Navigation Hub generato:
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);
}
}
Come puoi vedere, il Navigation Hub ha due tab, Home e Settings.
Il metodo layout restituisce il tipo di layout per l'hub. Riceve un BuildContext così puoi accedere ai dati del tema e alle media query durante la configurazione del layout.
Puoi creare ulteriori tab aggiungendo NavigationTab al Navigation Hub.
Per prima cosa, devi creare un nuovo widget usando Metro.
metro make:stateful_widget news_tab
Puoi anche creare più widget contemporaneamente.
metro make:stateful_widget news_tab,notifications_tab
Poi, puoi aggiungere il nuovo widget al 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()),
});
Per utilizzare il Navigation Hub, aggiungilo al tuo router come route iniziale:
import 'package:nylo_framework/nylo_framework.dart';
appRouter() => nyRoutes((router) {
...
router.add(BaseNavigationHub.path).initialRoute();
});
// oppure naviga verso il Navigation Hub da qualsiasi punto della tua app
routeTo(BaseNavigationHub.path);
Ci sono molte altre cose che puoi fare con un Navigation Hub, vediamo alcune delle funzionalità disponibili.
Navigazione Inferiore
Puoi impostare il layout su una barra di navigazione inferiore restituendo NavigationHubLayout.bottomNav dal metodo layout.
class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
...
@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav();
Puoi personalizzare la barra di navigazione inferiore impostando proprietà come le seguenti:
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,
);
Puoi applicare uno stile preimpostato alla barra di navigazione inferiore utilizzando il parametro style.
@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.bottomNav(
style: BottomNavStyle.material(), // Default Flutter material style
);
Nav Bar Builder Personalizzata
Per avere il controllo completo sulla tua barra di navigazione, puoi utilizzare il parametro navBarBuilder.
Questo ti permette di costruire qualsiasi widget personalizzato ricevendo comunque i dati di navigazione.
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'oggetto NavBarData contiene:
| Proprieta' | Tipo | Descrizione |
|---|---|---|
items |
List<BottomNavigationBarItem> |
Gli elementi della barra di navigazione |
currentIndex |
int |
L'indice attualmente selezionato |
onTap |
ValueChanged<int> |
Callback quando un tab viene toccato |
Ecco un esempio di una nav bar personalizzata con effetto vetro:
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,
),
),
),
),
);
},
)
Nota: Quando si utilizza
navBarBuilder, il parametrostyleviene ignorato.
Navigazione Superiore
Puoi cambiare il layout in una barra di navigazione superiore restituendo NavigationHubLayout.topNav dal metodo layout.
class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
...
@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.topNav();
Puoi personalizzare la barra di navigazione superiore impostando proprietà come le seguenti:
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,
);
Navigazione Journey
Puoi cambiare il layout in una navigazione journey restituendo NavigationHubLayout.journey dal metodo layout.
Questo e' ideale per flussi di onboarding o form con piu' passaggi.
class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
...
@override
NavigationHubLayout? layout(BuildContext context) => NavigationHubLayout.journey(
progressStyle: JourneyProgressStyle(
indicator: JourneyProgressIndicator.segments(),
),
);
Puoi anche impostare un backgroundGradient per il layout 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(),
),
);
Nota: Quando
backgroundGradiente' impostato, ha la precedenza subackgroundColor.
Se vuoi utilizzare il layout di navigazione journey, i tuoi widget dovrebbero usare JourneyState poiche' contiene molti metodi helper per gestire il journey.
Puoi creare l'intero journey usando il comando make:navigation_hub con il layout journey_states:
metro make:navigation_hub onboarding
# Seleziona: journey_states
# Inserisci: welcome, personal_info, add_photos
Questo creera' l'hub e tutti i widget journey state nella directory resources/pages/navigation_hubs/onboarding/states/.
Oppure puoi creare singoli widget journey usando:
metro make:journey_widget welcome,phone_number_step,add_photos_step
Poi puoi aggiungere i nuovi widget al Navigation Hub.
_MyNavigationHubState() : super(() => {
0: NavigationTab.journey(
page: Welcome(),
),
1: NavigationTab.journey(
page: PhoneNumberStep(),
),
2: NavigationTab.journey(
page: AddPhotosStep(),
),
});
Stili di Avanzamento del Journey
Puoi personalizzare lo stile dell'indicatore di avanzamento usando 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,
),
),
);
Puoi utilizzare i seguenti indicatori di avanzamento:
JourneyProgressIndicator.none(): Non mostra nulla - utile per nascondere l'indicatore su un tab specifico.JourneyProgressIndicator.linear(): Barra di avanzamento lineare.JourneyProgressIndicator.dots(): Indicatore di avanzamento a punti.JourneyProgressIndicator.numbered(): Indicatore di avanzamento con numeri.JourneyProgressIndicator.segments(): Barra di avanzamento segmentata.JourneyProgressIndicator.circular(): Indicatore di avanzamento circolare.JourneyProgressIndicator.timeline(): Indicatore di avanzamento stile timeline.JourneyProgressIndicator.custom(): Indicatore di avanzamento personalizzato tramite una funzione 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,
);
},
),
),
);
Puoi personalizzare la posizione e il padding dell'indicatore di avanzamento all'interno di 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),
),
);
Puoi utilizzare le seguenti posizioni per l'indicatore di avanzamento:
ProgressIndicatorPosition.top: Indicatore di avanzamento nella parte superiore dello schermo.ProgressIndicatorPosition.bottom: Indicatore di avanzamento nella parte inferiore dello schermo.
Override dello Stile di Avanzamento per Singolo Tab
Puoi sovrascrivere il progressStyle a livello di layout su singoli tab usando NavigationTab.journey(progressStyle: ...). I tab senza un proprio progressStyle ereditano quello predefinito del layout. I tab senza un valore predefinito a livello di layout e senza stile specifico non mostreranno un indicatore di avanzamento.
_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 estende NyState con funzionalita' specifiche per i journey, rendendo piu' semplice la creazione di flussi di onboarding e percorsi multi-step.
Per creare un nuovo JourneyState, puoi usare il comando seguente.
metro make:journey_widget onboard_user_dob
Oppure, se vuoi creare piu' widget contemporaneamente, puoi usare il comando seguente.
metro make:journey_widget welcome,phone_number_step,add_photos_step
Ecco come appare un widget JourneyState generato:
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 {}
}
Noterai che la classe JourneyState usa nextStep per navigare avanti e onBackPressed per tornare indietro.
Il metodo nextStep esegue l'intero ciclo di vita della validazione: canContinue() -> onBeforeNext() -> naviga (oppure onComplete() se si e' all'ultimo step) -> onAfterNext().
Puoi anche usare buildJourneyContent per costruire un layout strutturato con pulsanti di navigazione opzionali:
@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,
),
);
}
Ecco le proprieta' che puoi usare nel metodo buildJourneyContent.
| Proprieta' | Tipo | Descrizione |
|---|---|---|
content |
Widget |
Il contenuto principale della pagina. |
nextButton |
Widget? |
Il widget del pulsante avanti. |
backButton |
Widget? |
Il widget del pulsante indietro. |
contentPadding |
EdgeInsetsGeometry |
Il padding del contenuto. |
header |
Widget? |
Il widget dell'header. |
footer |
Widget? |
Il widget del footer. |
crossAxisAlignment |
CrossAxisAlignment |
L'allineamento dell'asse trasversale del contenuto. |
Metodi Helper di JourneyState
La classe JourneyState dispone di metodi helper e proprieta' che puoi usare per personalizzare il comportamento del tuo journey.
| Metodo / Proprieta' | Descrizione |
|---|---|
nextStep() |
Naviga allo step successivo con validazione. Restituisce Future<bool>. |
previousStep() |
Naviga allo step precedente. Restituisce Future<bool>. |
onBackPressed() |
Helper semplice per navigare allo step precedente. |
onComplete() |
Chiamato quando il journey e' completato (all'ultimo step). |
onBeforeNext() |
Chiamato prima di navigare allo step successivo. |
onAfterNext() |
Chiamato dopo aver navigato allo step successivo. |
canContinue() |
Controllo di validazione prima di navigare allo step successivo. |
isFirstStep |
Restituisce true se questo e' il primo step del journey. |
isLastStep |
Restituisce true se questo e' l'ultimo step del journey. |
currentStep |
Restituisce l'indice dello step corrente (base 0). |
totalSteps |
Restituisce il numero totale di step. |
completionPercentage |
Restituisce la percentuale di completamento (da 0.0 a 1.0). |
goToStep(int index) |
Salta a uno step specifico tramite indice. |
goToNextStep() |
Salta allo step successivo (senza validazione). |
goToPreviousStep() |
Salta allo step precedente (senza validazione). |
goToFirstStep() |
Salta al primo step. |
goToLastStep() |
Salta all'ultimo step. |
exitJourney() |
Esce dal journey eseguendo il pop del navigator principale. |
resetCurrentStep() |
Resetta lo stato dello step corrente. |
onJourneyComplete |
Callback al completamento del journey (da sovrascrivere nell'ultimo step). |
buildJourneyPage() |
Costruisce una pagina journey a schermo intero con Scaffold. |
nextStep
Il metodo nextStep naviga allo step successivo con validazione completa. Esegue il ciclo di vita: canContinue() -> onBeforeNext() -> naviga oppure onComplete() -> onAfterNext().
Puoi passare force: true per saltare la validazione e navigare direttamente.
@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
),
);
}
Per saltare la validazione:
onPressed: () => nextStep(force: true),
previousStep
Il metodo previousStep naviga allo step precedente. Restituisce true in caso di successo, false se si e' gia' al primo step.
onPressed: () async {
bool success = await previousStep();
if (!success) {
// Already at first step
}
},
onBackPressed
Il metodo onBackPressed e' un helper semplice che chiama internamente previousStep().
@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
Il metodo onComplete viene chiamato quando nextStep() viene invocato sull'ultimo step (dopo che la validazione e' superata).
@override
Future<void> onComplete() async {
print("Journey completed");
}
onBeforeNext
Il metodo onBeforeNext viene chiamato prima di navigare allo step successivo.
Ad esempio, se vuoi salvare dei dati prima di passare allo step successivo, puoi farlo qui.
@override
Future<void> onBeforeNext() async {
// E.g. save data to session
// session('onboarding', {
// 'name': 'Anthony Gordon',
// 'occupation': 'Software Engineer',
// });
}
onAfterNext
Il metodo onAfterNext viene chiamato dopo aver navigato allo step successivo.
@override
Future<void> onAfterNext() async {
// print('Navigated to the next step');
}
canContinue
Il metodo canContinue viene chiamato quando nextStep() viene invocato. Restituisci false per impedire la navigazione.
@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 proprieta' isFirstStep restituisce true se questo e' il primo step del journey.
backButton: isFirstStep ? null : Button.textOnly(
text: "Back",
textColor: Colors.black87,
onPressed: onBackPressed,
),
isLastStep
La proprieta' isLastStep restituisce true se questo e' l'ultimo step del journey.
nextButton: Button.primary(
text: isLastStep ? "Get Started" : "Continue",
onPressed: nextStep,
),
currentStep
La proprieta' currentStep restituisce l'indice dello step corrente (base 0).
Text("Step ${currentStep + 1} of $totalSteps"),
totalSteps
La proprieta' totalSteps restituisce il numero totale di step nel journey.
completionPercentage
La proprieta' completionPercentage restituisce la percentuale di completamento come valore da 0.0 a 1.0.
LinearProgressIndicator(value: completionPercentage),
goToStep
Il metodo goToStep salta direttamente a uno step specifico tramite indice. Questo non attiva la validazione.
nextButton: Button.primary(
text: "Skip to photos",
onPressed: () {
goToStep(2); // jump to step index 2
},
),
goToNextStep
Il metodo goToNextStep salta allo step successivo senza validazione. Se si e' gia' all'ultimo step, non fa nulla.
onPressed: () {
goToNextStep(); // skip validation and go to next step
},
goToPreviousStep
Il metodo goToPreviousStep salta allo step precedente senza validazione. Se si e' gia' al primo step, non fa nulla.
onPressed: () {
goToPreviousStep();
},
goToFirstStep
Il metodo goToFirstStep salta al primo step.
onPressed: () {
goToFirstStep();
},
goToLastStep
Il metodo goToLastStep salta all'ultimo step.
onPressed: () {
goToLastStep();
},
exitJourney
Il metodo exitJourney esce dal journey eseguendo il pop del navigator principale.
onPressed: () {
exitJourney(); // pop the root navigator
},
resetCurrentStep
Il metodo resetCurrentStep resetta lo stato dello step corrente.
onPressed: () {
resetCurrentStep();
},
onJourneyComplete
Il getter onJourneyComplete puo' essere sovrascritto nell'ultimo step del tuo journey per definire cosa succede quando l'utente completa il flusso.
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
Il metodo buildJourneyPage costruisce una pagina journey a schermo intero racchiusa in uno Scaffold con 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,
);
}
| Proprieta' | Tipo | Descrizione |
|---|---|---|
content |
Widget |
Il contenuto principale della pagina. |
nextButton |
Widget? |
Il widget del pulsante avanti. |
backButton |
Widget? |
Il widget del pulsante indietro. |
contentPadding |
EdgeInsetsGeometry |
Il padding del contenuto. |
header |
Widget? |
Il widget dell'header. |
footer |
Widget? |
Il widget del footer. |
backgroundColor |
Color? |
Il colore di sfondo dello Scaffold. |
appBar |
Widget? |
Un widget AppBar opzionale. |
crossAxisAlignment |
CrossAxisAlignment |
L'allineamento dell'asse trasversale del contenuto. |
Navigazione verso widget all'interno di un tab
Puoi navigare verso widget all'interno di un tab usando l'helper pushTo.
All'interno del tuo tab, puoi usare l'helper pushTo per navigare verso un altro widget.
_HomeTabState extends State<HomeTab> {
...
void _navigateToSettings() {
pushTo(SettingsPage());
}
...
}
Puoi anche passare dati al widget verso cui stai navigando.
_HomeTabState extends State<HomeTab> {
...
void _navigateToSettings() {
pushTo(SettingsPage(), data: {"name": "Anthony"});
}
...
}
Tab
I tab sono gli elementi fondamentali di un Navigation Hub.
Puoi aggiungere tab a un Navigation Hub usando la classe NavigationTab e i suoi costruttori nominati.
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),
),
});
Nell'esempio sopra, abbiamo aggiunto due tab al Navigation Hub, Home e Settings.
Puoi usare diversi tipi di tab:
NavigationTab.tab()- Un tab di navigazione standard.NavigationTab.badge()- Un tab con contatore badge.NavigationTab.alert()- Un tab con indicatore di alert.NavigationTab.journey()- Un tab per layout di navigazione journey.
Aggiungere Badge ai Tab
Abbiamo reso semplice aggiungere badge ai tuoi tab.
I badge sono un ottimo modo per mostrare agli utenti che c'e' qualcosa di nuovo in un tab.
Ad esempio, se hai un'app di chat, puoi mostrare il numero di messaggi non letti nel tab della chat.
Per aggiungere un badge a un tab, puoi usare il costruttore 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),
),
});
Nell'esempio sopra, abbiamo aggiunto un badge al tab Chat con un conteggio iniziale di 10.
Puoi anche aggiornare il conteggio del badge in modo programmatico.
/// 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);
Per impostazione predefinita, il conteggio del badge viene memorizzato. Se vuoi azzerare il conteggio del badge ad ogni sessione, puoi impostare rememberCount su false.
0: NavigationTab.badge(
title: "Chats",
page: ChatTab(),
icon: Icon(Icons.message),
activeIcon: Icon(Icons.message),
initialCount: 10,
rememberCount: false,
),
Aggiungere Alert ai Tab
Puoi aggiungere alert ai tuoi tab.
A volte potresti non voler mostrare un contatore badge, ma vuoi mostrare un indicatore di alert all'utente.
Per aggiungere un alert a un tab, puoi usare il costruttore 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),
),
});
Questo aggiungera' un alert al tab Chat con colore rosso.
Puoi anche aggiornare l'alert in modo programmatico.
/// Enable the alert
BaseNavigationHub.stateActions.alertEnableTab(tab: 0);
/// Disable the alert
BaseNavigationHub.stateActions.alertDisableTab(tab: 0);
Indice Iniziale
Per impostazione predefinita, il Navigation Hub parte dal primo tab (indice 0). Puoi cambiarlo sovrascrivendo il getter initialIndex.
class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
...
@override
int get initialIndex => 1; // Start on the second tab
...
}
Mantenimento dello stato
Per impostazione predefinita, lo stato del Navigation Hub viene mantenuto.
Questo significa che quando navighi verso un tab, lo stato del tab viene preservato.
Se vuoi cancellare lo stato del tab ogni volta che ci navighi, puoi impostare maintainState su false.
class _MyNavigationHubState extends NavigationHub<MyNavigationHub> {
...
@override
bool get maintainState => false;
...
}
onTap
Puoi sovrascrivere il metodo onTap per aggiungere logica personalizzata quando un tab viene toccato.
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 Actions
Le state actions sono un modo per interagire con il Navigation Hub da qualsiasi punto della tua app.
Ecco le state actions che puoi utilizzare:
/// 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();
Per utilizzare una state action, puoi fare quanto segue:
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
Stile di Caricamento
Fin da subito, il Navigation Hub mostrera' il tuo Widget di caricamento predefinito (resources/widgets/loader_widget.dart) durante il caricamento del tab.
Puoi personalizzare il loadingStyle per aggiornare lo stile di caricamento.
| Stile | Descrizione |
|---|---|
| normal | Stile di caricamento predefinito |
| skeletonizer | Stile di caricamento skeleton |
| none | Nessuno stile di caricamento |
Puoi cambiare lo stile di caricamento in questo modo:
@override
LoadingStyle get loadingStyle => LoadingStyle.normal();
// or
@override
LoadingStyle get loadingStyle => LoadingStyle.skeletonizer();
Se vuoi aggiornare il Widget di caricamento in uno degli stili, puoi passare un child al LoadingStyle.
@override
LoadingStyle get loadingStyle => LoadingStyle.normal(
child: Center(
child: Text("Loading..."),
),
);
Ora, quando il tab e' in caricamento, verra' visualizzato il testo "Loading...".
Esempio qui sotto:
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..."),
),
);
...
}
Creazione di un Navigation Hub
Per creare un Navigation Hub, puoi usare Metro, utilizza il comando seguente.
metro make:navigation_hub base
Il comando ti guidera' attraverso una configurazione interattiva in cui puoi scegliere il tipo di layout e definire i tuoi tab o journey state.
Questo creera' un file base_navigation_hub.dart nella directory resources/pages/navigation_hubs/base/ con i widget figli organizzati nelle sottocartelle tabs/ o states/.