Widgets

Navigation Hub

परिचय

Navigation Hubs एक केंद्रीय स्थान हैं जहाँ आप अपने सभी विजेट्स के लिए नेविगेशन प्रबंधित कर सकते हैं। बिल्ट-इन सुविधा के साथ आप सेकंडों में bottom, top और journey नेविगेशन लेआउट बना सकते हैं।

आइए कल्पना करें कि आपके पास एक ऐप है और आप एक bottom navigation bar जोड़ना चाहते हैं ताकि यूज़र्स आपके ऐप में विभिन्न टैब्स के बीच नेविगेट कर सकें।

इसे बनाने के लिए आप एक Navigation Hub का उपयोग कर सकते हैं।

आइए जानें कि आप अपने ऐप में Navigation Hub कैसे उपयोग कर सकते हैं।

बेसिक उपयोग

आप नीचे दिए गए कमांड का उपयोग करके एक Navigation Hub बना सकते हैं।

metro make:navigation_hub base

यह कमांड आपको एक interactive setup से गुज़ारेगा:

  1. लेआउट टाइप चुनें - navigation_tabs (bottom navigation) या journey_states (sequential flow) में से चुनें।
  2. टैब/स्टेट के नाम दर्ज करें - अपने टैब्स या journey states के लिए कॉमा-सेपरेटेड नाम दें।

यह आपकी resources/pages/navigation_hubs/base/ डायरेक्टरी में फ़ाइलें बनाएगा:

  • base_navigation_hub.dart - मुख्य hub विजेट
  • tabs/ या states/ - प्रत्येक टैब या journey state के लिए child विजेट्स

एक जनरेट किया हुआ Navigation Hub कैसा दिखता है:

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

आप देख सकते हैं कि इस Navigation Hub में दो टैब हैं, Home और Settings।

layout मेथड hub के लिए लेआउट टाइप रिटर्न करता है। इसे एक BuildContext मिलता है ताकि आप अपना लेआउट कॉन्फ़िगर करते समय theme data और media queries एक्सेस कर सकें।

आप Navigation Hub में NavigationTab जोड़कर और टैब्स बना सकते हैं।

सबसे पहले, आपको Metro का उपयोग करके एक नया विजेट बनाना होगा।

metro make:stateful_widget news_tab

आप एक साथ कई विजेट्स भी बना सकते हैं।

metro make:stateful_widget news_tab,notifications_tab

फिर, आप नए विजेट को 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()),
});

Navigation Hub को उपयोग करने के लिए, इसे अपने राउटर में initial route के रूप में जोड़ें:

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

Navigation Hub के साथ और भी बहुत कुछ किया जा सकता है, आइए कुछ फ़ीचर्स को विस्तार से जानें।

बॉटम नेविगेशन

आप layout मेथड से NavigationHubLayout.bottomNav रिटर्न करके लेआउट को bottom navigation bar में सेट कर सकते हैं।

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

आप निम्नलिखित प्रॉपर्टीज़ सेट करके bottom navigation bar को कस्टमाइज़ कर सकते हैं:

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

आप style पैरामीटर का उपयोग करके अपनी bottom navigation bar पर प्रीसेट स्टाइल लागू कर सकते हैं।

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

कस्टम Nav Bar बिल्डर

अपनी navigation bar पर पूर्ण नियंत्रण के लिए, आप navBarBuilder पैरामीटर का उपयोग कर सकते हैं।

यह आपको नेविगेशन डेटा प्राप्त करते हुए कोई भी कस्टम विजेट बनाने की अनुमति देता है।

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

NavBarData ऑब्जेक्ट में निम्नलिखित शामिल हैं:

प्रॉपर्टी टाइप विवरण
items List<BottomNavigationBarItem> नेविगेशन बार आइटम्स
currentIndex int वर्तमान में चयनित इंडेक्स
onTap ValueChanged<int> टैब टैप होने पर कॉलबैक

यहाँ एक पूरी तरह कस्टम glass nav bar का उदाहरण है:

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

नोट: navBarBuilder का उपयोग करते समय, style पैरामीटर को अनदेखा किया जाता है।

टॉप नेविगेशन

आप layout मेथड से NavigationHubLayout.topNav रिटर्न करके लेआउट को top navigation bar में बदल सकते हैं।

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

आप निम्नलिखित प्रॉपर्टीज़ सेट करके top navigation bar को कस्टमाइज़ कर सकते हैं:

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 नेविगेशन

आप layout मेथड से NavigationHubLayout.journey रिटर्न करके लेआउट को journey navigation में बदल सकते हैं।

यह onboarding flows या multi-step forms के लिए बहुत उपयुक्त है।

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

आप journey लेआउट के लिए backgroundGradient भी सेट कर सकते हैं:

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

नोट: जब backgroundGradient सेट होता है, तो यह backgroundColor से ऊपर प्राथमिकता लेता है।

यदि आप journey navigation लेआउट का उपयोग करना चाहते हैं, तो आपके विजेट्स को JourneyState का उपयोग करना चाहिए क्योंकि इसमें journey प्रबंधित करने के लिए बहुत से हेल्पर मेथड्स हैं।

आप make:navigation_hub कमांड के साथ journey_states लेआउट चुनकर पूरी journey बना सकते हैं:

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

यह hub और सभी journey state विजेट्स resources/pages/navigation_hubs/onboarding/states/ के अंदर बनाएगा।

या आप अलग-अलग journey विजेट्स इस तरह बना सकते हैं:

metro make:journey_widget welcome,phone_number_step,add_photos_step

फिर आप नए विजेट्स को Navigation Hub में जोड़ सकते हैं।

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

Journey प्रोग्रेस स्टाइल्स

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

आप निम्नलिखित प्रोग्रेस इंडिकेटर्स का उपयोग कर सकते हैं:

  • JourneyProgressIndicator.none(): कुछ भी रेंडर नहीं करता - किसी विशिष्ट टैब पर इंडिकेटर छिपाने के लिए उपयोगी।
  • JourneyProgressIndicator.linear(): लिनियर प्रोग्रेस बार।
  • JourneyProgressIndicator.dots(): डॉट्स-आधारित प्रोग्रेस इंडिकेटर।
  • JourneyProgressIndicator.numbered(): नंबर्ड स्टेप प्रोग्रेस इंडिकेटर।
  • JourneyProgressIndicator.segments(): सेगमेंटेड प्रोग्रेस बार स्टाइल।
  • JourneyProgressIndicator.circular(): सर्कुलर प्रोग्रेस इंडिकेटर।
  • JourneyProgressIndicator.timeline(): टाइमलाइन-स्टाइल प्रोग्रेस इंडिकेटर।
  • JourneyProgressIndicator.custom(): बिल्डर फ़ंक्शन का उपयोग करके कस्टम प्रोग्रेस इंडिकेटर।
@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,
                );
            },
        ),
    ),
);

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

आप निम्नलिखित प्रोग्रेस इंडिकेटर पोज़िशन का उपयोग कर सकते हैं:

  • ProgressIndicatorPosition.top: स्क्रीन के शीर्ष पर प्रोग्रेस इंडिकेटर।
  • ProgressIndicatorPosition.bottom: स्क्रीन के निचले भाग पर प्रोग्रेस इंडिकेटर।

प्रति-टैब प्रोग्रेस स्टाइल ओवरराइड

आप NavigationTab.journey(progressStyle: ...) का उपयोग करके अलग-अलग टैब्स पर लेआउट-स्तरीय progressStyle को ओवरराइड कर सकते हैं। जिन टैब्स का अपना progressStyle नहीं है, वे लेआउट डिफ़ॉल्ट इनहेरिट करते हैं। बिना लेआउट डिफ़ॉल्ट और बिना प्रति-टैब स्टाइल वाले टैब्स प्रोग्रेस इंडिकेटर नहीं दिखाएंगे।

_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

JourneyState क्लास NyState को journey-विशिष्ट कार्यक्षमता के साथ extend करती है ताकि onboarding flows और multi-step journeys बनाना आसान हो।

नया JourneyState बनाने के लिए, आप नीचे दिए गए कमांड का उपयोग कर सकते हैं।

metro make:journey_widget onboard_user_dob

या यदि आप एक साथ कई विजेट्स बनाना चाहते हैं, तो आप निम्नलिखित कमांड का उपयोग कर सकते हैं।

metro make:journey_widget welcome,phone_number_step,add_photos_step

एक जनरेट किया हुआ JourneyState विजेट कैसा दिखता है:

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

आप देखेंगे कि JourneyState क्लास आगे नेविगेट करने के लिए nextStep और पीछे जाने के लिए onBackPressed का उपयोग करती है।

nextStep मेथड पूरे validation lifecycle से गुज़रता है: canContinue() -> onBeforeNext() -> नेविगेट (या अंतिम स्टेप पर onComplete()) -> onAfterNext()

आप buildJourneyContent का उपयोग करके optional navigation buttons के साथ एक structured layout भी बना सकते हैं:

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

यहाँ buildJourneyContent मेथड में उपयोग की जा सकने वाली प्रॉपर्टीज़ हैं।

प्रॉपर्टी टाइप विवरण
content Widget पेज की मुख्य सामग्री।
nextButton Widget? नेक्स्ट बटन विजेट।
backButton Widget? बैक बटन विजेट।
contentPadding EdgeInsetsGeometry कंटेंट के लिए पैडिंग।
header Widget? हेडर विजेट।
footer Widget? फुटर विजेट।
crossAxisAlignment CrossAxisAlignment कंटेंट का cross axis alignment।

JourneyState हेल्पर मेथड्स

JourneyState क्लास में हेल्पर मेथड्स और प्रॉपर्टीज़ हैं जिनका उपयोग आप अपनी journey के व्यवहार को कस्टमाइज़ करने के लिए कर सकते हैं।

मेथड / प्रॉपर्टी विवरण
nextStep() validation के साथ अगले स्टेप पर नेविगेट करें। Future<bool> रिटर्न करता है।
previousStep() पिछले स्टेप पर नेविगेट करें। Future<bool> रिटर्न करता है।
onBackPressed() पिछले स्टेप पर नेविगेट करने के लिए सरल हेल्पर।
onComplete() journey पूरी होने पर कॉल होता है (अंतिम स्टेप पर)।
onBeforeNext() अगले स्टेप पर नेविगेट करने से पहले कॉल होता है।
onAfterNext() अगले स्टेप पर नेविगेट करने के बाद कॉल होता है।
canContinue() अगले स्टेप पर नेविगेट करने से पहले validation चेक।
isFirstStep यदि यह journey का पहला स्टेप है तो true रिटर्न करता है।
isLastStep यदि यह journey का अंतिम स्टेप है तो true रिटर्न करता है।
currentStep वर्तमान स्टेप इंडेक्स (0-based) रिटर्न करता है।
totalSteps कुल स्टेप्स की संख्या रिटर्न करता है।
completionPercentage पूर्णता प्रतिशत (0.0 से 1.0) रिटर्न करता है।
goToStep(int index) इंडेक्स द्वारा किसी विशिष्ट स्टेप पर सीधे जाएं।
goToNextStep() अगले स्टेप पर जाएं (बिना validation)।
goToPreviousStep() पिछले स्टेप पर जाएं (बिना validation)।
goToFirstStep() पहले स्टेप पर जाएं।
goToLastStep() अंतिम स्टेप पर जाएं।
exitJourney() root navigator को pop करके journey से बाहर निकलें।
resetCurrentStep() वर्तमान स्टेप की state रीसेट करें।
onJourneyComplete journey पूरी होने पर कॉलबैक (अंतिम स्टेप में ओवरराइड करें)।
buildJourneyPage() Scaffold के साथ फुल-स्क्रीन journey पेज बनाएं।

nextStep

nextStep मेथड पूर्ण validation के साथ अगले स्टेप पर नेविगेट करता है। यह lifecycle से गुज़रता है: canContinue() -> onBeforeNext() -> नेविगेट या onComplete() -> onAfterNext()

आप force: true पास करके validation बायपास करके सीधे नेविगेट कर सकते हैं।

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

validation स्किप करने के लिए:

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

previousStep

previousStep मेथड पिछले स्टेप पर नेविगेट करता है। सफल होने पर true रिटर्न करता है, पहले स्टेप पर पहले से होने पर false

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

onBackPressed

onBackPressed मेथड एक सरल हेल्पर है जो internally 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

onComplete मेथड तब कॉल होता है जब अंतिम स्टेप पर nextStep() ट्रिगर होता है (validation पास होने के बाद)।

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

onBeforeNext

onBeforeNext मेथड अगले स्टेप पर नेविगेट करने से पहले कॉल होता है।

उदा. यदि आप अगले स्टेप पर जाने से पहले डेटा सेव करना चाहते हैं, तो आप यहाँ कर सकते हैं।

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

onAfterNext

onAfterNext मेथड अगले स्टेप पर नेविगेट करने के बाद कॉल होता है।

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

canContinue

canContinue मेथड तब कॉल होता है जब nextStep() ट्रिगर होता है। नेविगेशन रोकने के लिए false रिटर्न करें।

@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

isFirstStep प्रॉपर्टी true रिटर्न करती है यदि यह journey का पहला स्टेप है।

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

isLastStep

isLastStep प्रॉपर्टी true रिटर्न करती है यदि यह journey का अंतिम स्टेप है।

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

currentStep

currentStep प्रॉपर्टी वर्तमान स्टेप इंडेक्स (0-based) रिटर्न करती है।

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

totalSteps

totalSteps प्रॉपर्टी journey में कुल स्टेप्स की संख्या रिटर्न करती है।

completionPercentage

completionPercentage प्रॉपर्टी पूर्णता प्रतिशत 0.0 से 1.0 के बीच एक वैल्यू के रूप में रिटर्न करती है।

LinearProgressIndicator(value: completionPercentage),

goToStep

goToStep मेथड इंडेक्स द्वारा सीधे किसी विशिष्ट स्टेप पर ले जाता है। यह validation ट्रिगर नहीं करता।

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

goToNextStep

goToNextStep मेथड बिना validation के अगले स्टेप पर ले जाता है। यदि पहले से अंतिम स्टेप पर हैं, तो कुछ नहीं करता।

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

goToPreviousStep

goToPreviousStep मेथड बिना validation के पिछले स्टेप पर ले जाता है। यदि पहले से पहले स्टेप पर हैं, तो कुछ नहीं करता।

onPressed: () {
    goToPreviousStep();
},

goToFirstStep

goToFirstStep मेथड पहले स्टेप पर ले जाता है।

onPressed: () {
    goToFirstStep();
},

goToLastStep

goToLastStep मेथड अंतिम स्टेप पर ले जाता है।

onPressed: () {
    goToLastStep();
},

exitJourney

exitJourney मेथड root navigator को pop करके journey से बाहर निकालता है।

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

resetCurrentStep

resetCurrentStep मेथड वर्तमान स्टेप की state रीसेट करता है।

onPressed: () {
    resetCurrentStep();
},

onJourneyComplete

onJourneyComplete getter को आपकी journey के अंतिम स्टेप में ओवरराइड किया जा सकता है ताकि यह परिभाषित किया जा सके कि यूज़र flow पूरा करने पर क्या होगा।

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

buildJourneyPage मेथड Scaffold और SafeArea में wrapped एक फुल-स्क्रीन journey पेज बनाता है।

@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,
    );
}
प्रॉपर्टी टाइप विवरण
content Widget पेज की मुख्य सामग्री।
nextButton Widget? नेक्स्ट बटन विजेट।
backButton Widget? बैक बटन विजेट।
contentPadding EdgeInsetsGeometry कंटेंट के लिए पैडिंग।
header Widget? हेडर विजेट।
footer Widget? फुटर विजेट।
backgroundColor Color? Scaffold का बैकग्राउंड कलर।
appBar Widget? एक optional AppBar विजेट।
crossAxisAlignment CrossAxisAlignment कंटेंट का cross axis alignment।

टैब के भीतर विजेट्स पर नेविगेट करना

आप pushTo हेल्पर का उपयोग करके टैब के भीतर विजेट्स पर नेविगेट कर सकते हैं।

अपने टैब के अंदर, आप दूसरे विजेट पर नेविगेट करने के लिए pushTo हेल्पर का उपयोग कर सकते हैं।

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

आप जिस विजेट पर नेविगेट कर रहे हैं उसे डेटा भी पास कर सकते हैं।

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

टैब्स

टैब्स एक Navigation Hub के मुख्य building blocks हैं।

आप NavigationTab क्लास और इसके named constructors का उपयोग करके Navigation Hub में टैब्स जोड़ सकते हैं।

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

ऊपर के उदाहरण में, हमने Navigation Hub में दो टैब्स जोड़े हैं, Home और Settings।

आप विभिन्न प्रकार के टैब्स का उपयोग कर सकते हैं:

  • NavigationTab.tab() - एक स्टैंडर्ड नेविगेशन टैब।
  • NavigationTab.badge() - badge count वाला टैब।
  • NavigationTab.alert() - alert indicator वाला टैब।
  • NavigationTab.journey() - journey navigation लेआउट के लिए टैब।

टैब्स में बैज जोड़ना

हमने आपके टैब्स में badges जोड़ना बहुत आसान बना दिया है।

Badges यूज़र्स को यह दिखाने का एक शानदार तरीका है कि किसी टैब में कुछ नया है।

उदाहरण, यदि आपके पास एक चैट ऐप है, तो आप चैट टैब में अपठित संदेशों की संख्या दिखा सकते हैं।

किसी टैब में badge जोड़ने के लिए, आप NavigationTab.badge constructor का उपयोग कर सकते हैं।

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

ऊपर के उदाहरण में, हमने 10 की प्रारंभिक गणना के साथ Chat टैब में एक badge जोड़ा है।

आप प्रोग्रामैटिक रूप से badge count भी अपडेट कर सकते हैं।

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

डिफ़ॉल्ट रूप से, badge count याद रखा जाएगा। यदि आप प्रत्येक session में badge count क्लियर करना चाहते हैं, तो आप rememberCount को false सेट कर सकते हैं।

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

टैब्स में अलर्ट जोड़ना

आप अपने टैब्स में alerts जोड़ सकते हैं।

कभी-कभी आप badge count नहीं दिखाना चाहते, लेकिन आप यूज़र को एक alert indicator दिखाना चाहते हैं।

किसी टैब में alert जोड़ने के लिए, आप NavigationTab.alert constructor का उपयोग कर सकते हैं।

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

यह लाल रंग के साथ Chat टैब में एक alert जोड़ देगा।

आप प्रोग्रामैटिक रूप से alert भी अपडेट कर सकते हैं।

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

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

प्रारंभिक इंडेक्स

डिफ़ॉल्ट रूप से, Navigation Hub पहले टैब (index 0) से शुरू होता है। आप initialIndex getter को ओवरराइड करके इसे बदल सकते हैं।

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

स्टेट बनाए रखना

डिफ़ॉल्ट रूप से, Navigation Hub की state बनाए रखी जाती है।

इसका मतलब है कि जब आप किसी टैब पर नेविगेट करते हैं, तो उस टैब की state संरक्षित रहती है।

यदि आप हर बार टैब पर जाने पर उसकी state क्लियर करना चाहते हैं, तो आप maintainState को false सेट कर सकते हैं।

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

onTap

आप onTap मेथड को ओवरराइड करके टैब टैप होने पर कस्टम लॉजिक जोड़ सकते हैं।

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 आपके ऐप में कहीं से भी Navigation Hub के साथ interact करने का एक तरीका है।

यहाँ उपलब्ध state actions हैं:

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

State action का उपयोग करने के लिए:

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

लोडिंग स्टाइल

बिल्ट-इन सुविधा के साथ, Navigation Hub टैब लोड होते समय आपका डिफ़ॉल्ट loading Widget (resources/widgets/loader_widget.dart) दिखाएगा।

आप loading style अपडेट करने के लिए loadingStyle कस्टमाइज़ कर सकते हैं।

स्टाइल विवरण
normal डिफ़ॉल्ट loading स्टाइल
skeletonizer Skeleton loading स्टाइल
none कोई loading स्टाइल नहीं

आप loading style इस तरह बदल सकते हैं:

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

यदि आप किसी style में loading Widget अपडेट करना चाहते हैं, तो आप LoadingStyle में एक child पास कर सकते हैं।

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

अब, जब टैब लोड हो रहा होगा, तो "Loading..." टेक्स्ट दिखाया जाएगा।

नीचे उदाहरण:

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

Navigation Hub बनाना

Navigation Hub बनाने के लिए, आप Metro का उपयोग कर सकते हैं, नीचे दिए गए कमांड का उपयोग करें।

metro make:navigation_hub base

यह कमांड आपको एक interactive setup से गुज़ारेगा जहाँ आप लेआउट टाइप चुन सकते हैं और अपने टैब्स या journey states परिभाषित कर सकते हैं।

यह आपकी resources/pages/navigation_hubs/base/ डायरेक्टरी में एक base_navigation_hub.dart फ़ाइल बनाएगा, जिसमें child विजेट्स tabs/ या states/ subfolder में व्यवस्थित होंगे।