Widgets

Navigation Hub

Pengantar

Navigation Hub adalah tempat sentral di mana Anda dapat mengelola navigasi untuk semua widget Anda. Secara langsung, Anda dapat membuat tata letak navigasi bawah, atas, dan journey dalam hitungan detik.

Mari bayangkan Anda memiliki aplikasi dan ingin menambahkan navigasi bar bawah serta memungkinkan pengguna berpindah antar tab di aplikasi Anda.

Anda dapat menggunakan Navigation Hub untuk membangun ini.

Mari kita lihat bagaimana cara menggunakan Navigation Hub di aplikasi Anda.

Penggunaan Dasar

Anda dapat membuat Navigation Hub menggunakan perintah di bawah ini.

metro make:navigation_hub base

Perintah ini akan memandu Anda melalui pengaturan interaktif:

  1. Pilih tipe layout - Pilih antara navigation_tabs (navigasi bawah) atau journey_states (alur berurutan).
  2. Masukkan nama tab/state - Berikan nama-nama yang dipisahkan koma untuk tab atau journey state Anda.

Ini akan membuat file di direktori resources/pages/navigation_hubs/base/ Anda:

  • base_navigation_hub.dart - Widget hub utama
  • tabs/ atau states/ - Berisi widget anak untuk setiap tab atau journey state

Berikut tampilan Navigation Hub yang dihasilkan:

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

Anda dapat melihat bahwa Navigation Hub memiliki dua tab, Home dan Settings.

Method layout mengembalikan tipe layout untuk hub. Method ini menerima BuildContext sehingga Anda dapat mengakses data tema dan media query saat mengonfigurasi layout Anda.

Anda dapat membuat lebih banyak tab dengan menambahkan NavigationTab ke Navigation Hub.

Pertama, Anda perlu membuat widget baru menggunakan Metro.

metro make:stateful_widget news_tab

Anda juga dapat membuat beberapa widget sekaligus.

metro make:stateful_widget news_tab,notifications_tab

Kemudian, Anda dapat menambahkan widget baru ke 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()),
});

Untuk menggunakan Navigation Hub, tambahkan ke router Anda sebagai rute awal:

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

Masih banyak lagi yang dapat Anda lakukan dengan Navigation Hub, mari kita bahas beberapa fiturnya.

Navigasi Bawah

Anda dapat mengatur layout menjadi navigasi bar bawah dengan mengembalikan NavigationHubLayout.bottomNav dari method layout.

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

Anda dapat menyesuaikan navigasi bar bawah dengan mengatur properti seperti berikut:

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

Anda dapat menerapkan gaya preset ke navigasi bar bawah Anda menggunakan parameter style.

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

Nav Bar Builder Kustom

Untuk kontrol penuh atas navigasi bar Anda, Anda dapat menggunakan parameter navBarBuilder.

Ini memungkinkan Anda membangun widget kustom apa pun sambil tetap menerima data navigasi.

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

Objek NavBarData berisi:

Properti Tipe Deskripsi
items List<BottomNavigationBarItem> Item navigasi bar
currentIndex int Indeks yang sedang dipilih
onTap ValueChanged<int> Callback saat tab diketuk

Berikut contoh navigasi bar glass kustom sepenuhnya:

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

Catatan: Saat menggunakan navBarBuilder, parameter style akan diabaikan.

Navigasi Atas

Anda dapat mengubah layout menjadi navigasi bar atas dengan mengembalikan NavigationHubLayout.topNav dari method layout.

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

Anda dapat menyesuaikan navigasi bar atas dengan mengatur properti seperti berikut:

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

Navigasi Journey

Anda dapat mengubah layout menjadi navigasi journey dengan mengembalikan NavigationHubLayout.journey dari method layout.

Ini sangat cocok untuk alur onboarding atau form multi-langkah.

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

Anda juga dapat mengatur backgroundGradient untuk 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(),
    ),
);

Catatan: Ketika backgroundGradient diatur, ia akan lebih diprioritaskan daripada backgroundColor.

Jika Anda ingin menggunakan layout navigasi journey, widget Anda sebaiknya menggunakan JourneyState karena berisi banyak method helper untuk membantu Anda mengelola journey.

Anda dapat membuat seluruh journey menggunakan perintah make:navigation_hub dengan layout journey_states:

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

Ini akan membuat hub dan semua widget journey state di resources/pages/navigation_hubs/onboarding/states/.

Atau Anda dapat membuat widget journey individual menggunakan:

metro make:journey_widget welcome,phone_number_step,add_photos_step

Anda kemudian dapat menambahkan widget baru ke Navigation Hub.

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

Gaya Progress Journey

Anda dapat menyesuaikan gaya indikator progress menggunakan kelas 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,
            ),
        ),
    );

Anda dapat menggunakan indikator progress berikut:

  • JourneyProgressIndicator.none(): Tidak merender apa pun - berguna untuk menyembunyikan indikator pada tab tertentu.
  • JourneyProgressIndicator.linear(): Indikator progress linear.
  • JourneyProgressIndicator.dots(): Indikator progress berbasis titik.
  • JourneyProgressIndicator.numbered(): Indikator progress langkah bernomor.
  • JourneyProgressIndicator.segments(): Gaya progress bar tersegmentasi.
  • JourneyProgressIndicator.circular(): Indikator progress melingkar.
  • JourneyProgressIndicator.timeline(): Indikator progress gaya timeline.
  • JourneyProgressIndicator.custom(): Indikator progress kustom menggunakan fungsi 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,
                );
            },
        ),
    ),
);

Anda dapat menyesuaikan posisi dan padding indikator progress di dalam 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),
    ),
);

Anda dapat menggunakan posisi indikator progress berikut:

  • ProgressIndicatorPosition.top: Indikator progress di bagian atas layar.
  • ProgressIndicatorPosition.bottom: Indikator progress di bagian bawah layar.

Override Gaya Progress Per-Tab

Anda dapat meng-override progressStyle pada level layout untuk tab individual menggunakan NavigationTab.journey(progressStyle: ...). Tab tanpa progressStyle sendiri akan mewarisi default layout. Tab tanpa default layout dan tanpa gaya per-tab tidak akan menampilkan indikator progress.

_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

Kelas JourneyState meng-extend NyState dengan fungsionalitas khusus journey untuk mempermudah pembuatan alur onboarding dan journey multi-langkah.

Untuk membuat JourneyState baru, Anda dapat menggunakan perintah di bawah ini.

metro make:journey_widget onboard_user_dob

Atau jika Anda ingin membuat beberapa widget sekaligus, Anda dapat menggunakan perintah berikut.

metro make:journey_widget welcome,phone_number_step,add_photos_step

Berikut tampilan widget JourneyState yang dihasilkan:

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

Anda akan melihat bahwa kelas JourneyState menggunakan nextStep untuk berpindah maju dan onBackPressed untuk kembali.

Method nextStep menjalankan seluruh siklus validasi: canContinue() -> onBeforeNext() -> navigasi (atau onComplete() jika di langkah terakhir) -> onAfterNext().

Anda juga dapat menggunakan buildJourneyContent untuk membangun layout terstruktur dengan tombol navigasi opsional:

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

Berikut properti yang dapat Anda gunakan di method buildJourneyContent.

Properti Tipe Deskripsi
content Widget Konten utama halaman.
nextButton Widget? Widget tombol lanjut.
backButton Widget? Widget tombol kembali.
contentPadding EdgeInsetsGeometry Padding untuk konten.
header Widget? Widget header.
footer Widget? Widget footer.
crossAxisAlignment CrossAxisAlignment Alignment sumbu silang konten.

Method Helper JourneyState

Kelas JourneyState memiliki method helper dan properti yang dapat Anda gunakan untuk menyesuaikan perilaku journey Anda.

Method / Properti Deskripsi
nextStep() Berpindah ke langkah berikutnya dengan validasi. Mengembalikan Future<bool>.
previousStep() Berpindah ke langkah sebelumnya. Mengembalikan Future<bool>.
onBackPressed() Helper sederhana untuk berpindah ke langkah sebelumnya.
onComplete() Dipanggil saat journey selesai (di langkah terakhir).
onBeforeNext() Dipanggil sebelum berpindah ke langkah berikutnya.
onAfterNext() Dipanggil setelah berpindah ke langkah berikutnya.
canContinue() Pengecekan validasi sebelum berpindah ke langkah berikutnya.
isFirstStep Mengembalikan true jika ini adalah langkah pertama dalam journey.
isLastStep Mengembalikan true jika ini adalah langkah terakhir dalam journey.
currentStep Mengembalikan indeks langkah saat ini (berbasis 0).
totalSteps Mengembalikan jumlah total langkah.
completionPercentage Mengembalikan persentase penyelesaian (0.0 hingga 1.0).
goToStep(int index) Langsung menuju langkah tertentu berdasarkan indeks.
goToNextStep() Langsung menuju langkah berikutnya (tanpa validasi).
goToPreviousStep() Langsung menuju langkah sebelumnya (tanpa validasi).
goToFirstStep() Langsung menuju langkah pertama.
goToLastStep() Langsung menuju langkah terakhir.
exitJourney() Keluar dari journey dengan melakukan pop pada root navigator.
resetCurrentStep() Mereset state langkah saat ini.
onJourneyComplete Callback saat journey selesai (override di langkah terakhir).
buildJourneyPage() Membangun halaman journey layar penuh dengan Scaffold.

nextStep

Method nextStep berpindah ke langkah berikutnya dengan validasi penuh. Method ini menjalankan siklus: canContinue() -> onBeforeNext() -> navigasi atau onComplete() -> onAfterNext().

Anda dapat memasukkan force: true untuk melewati validasi dan langsung berpindah.

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

Untuk melewati validasi:

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

previousStep

Method previousStep berpindah ke langkah sebelumnya. Mengembalikan true jika berhasil, false jika sudah di langkah pertama.

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

onBackPressed

Method onBackPressed adalah helper sederhana yang memanggil previousStep() secara internal.

@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

Method onComplete dipanggil saat nextStep() dipicu di langkah terakhir (setelah validasi berhasil).

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

onBeforeNext

Method onBeforeNext dipanggil sebelum berpindah ke langkah berikutnya.

Contoh: jika Anda ingin menyimpan data sebelum berpindah ke langkah berikutnya, Anda dapat melakukannya di sini.

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

onAfterNext

Method onAfterNext dipanggil setelah berpindah ke langkah berikutnya.

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

canContinue

Method canContinue dipanggil saat nextStep() dipicu. Kembalikan false untuk mencegah navigasi.

@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

Properti isFirstStep mengembalikan true jika ini adalah langkah pertama dalam journey.

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

isLastStep

Properti isLastStep mengembalikan true jika ini adalah langkah terakhir dalam journey.

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

currentStep

Properti currentStep mengembalikan indeks langkah saat ini (berbasis 0).

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

totalSteps

Properti totalSteps mengembalikan jumlah total langkah dalam journey.

completionPercentage

Properti completionPercentage mengembalikan persentase penyelesaian sebagai nilai dari 0.0 hingga 1.0.

LinearProgressIndicator(value: completionPercentage),

goToStep

Method goToStep langsung menuju langkah tertentu berdasarkan indeks. Method ini tidak memicu validasi.

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

goToNextStep

Method goToNextStep langsung menuju langkah berikutnya tanpa validasi. Jika sudah di langkah terakhir, method ini tidak melakukan apa-apa.

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

goToPreviousStep

Method goToPreviousStep langsung menuju langkah sebelumnya tanpa validasi. Jika sudah di langkah pertama, method ini tidak melakukan apa-apa.

onPressed: () {
    goToPreviousStep();
},

goToFirstStep

Method goToFirstStep langsung menuju langkah pertama.

onPressed: () {
    goToFirstStep();
},

goToLastStep

Method goToLastStep langsung menuju langkah terakhir.

onPressed: () {
    goToLastStep();
},

exitJourney

Method exitJourney keluar dari journey dengan melakukan pop pada root navigator.

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

resetCurrentStep

Method resetCurrentStep mereset state langkah saat ini.

onPressed: () {
    resetCurrentStep();
},

onJourneyComplete

Getter onJourneyComplete dapat di-override di langkah terakhir journey Anda untuk menentukan apa yang terjadi saat pengguna menyelesaikan alur tersebut.

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

Method buildJourneyPage membangun halaman journey layar penuh yang dibungkus dalam Scaffold dengan 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,
    );
}
Properti Tipe Deskripsi
content Widget Konten utama halaman.
nextButton Widget? Widget tombol lanjut.
backButton Widget? Widget tombol kembali.
contentPadding EdgeInsetsGeometry Padding untuk konten.
header Widget? Widget header.
footer Widget? Widget footer.
backgroundColor Color? Warna latar belakang Scaffold.
appBar Widget? Widget AppBar opsional.
crossAxisAlignment CrossAxisAlignment Alignment sumbu silang konten.

Navigasi ke widget di dalam tab

Anda dapat berpindah ke widget di dalam tab menggunakan helper pushTo.

Di dalam tab Anda, Anda dapat menggunakan helper pushTo untuk berpindah ke widget lain.

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

Anda juga dapat mengirim data ke widget yang Anda tuju.

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

Tab

Tab adalah blok bangunan utama dari Navigation Hub.

Anda dapat menambahkan tab ke Navigation Hub menggunakan kelas NavigationTab dan named constructor-nya.

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

Pada contoh di atas, kita menambahkan dua tab ke Navigation Hub, Home dan Settings.

Anda dapat menggunakan berbagai jenis tab:

  • NavigationTab.tab() - Tab navigasi standar.
  • NavigationTab.badge() - Tab dengan jumlah badge.
  • NavigationTab.alert() - Tab dengan indikator alert.
  • NavigationTab.journey() - Tab untuk layout navigasi journey.

Menambahkan Badge ke Tab

Kami telah memudahkan untuk menambahkan badge ke tab Anda.

Badge adalah cara yang bagus untuk menunjukkan kepada pengguna bahwa ada sesuatu yang baru di suatu tab.

Contohnya, jika Anda memiliki aplikasi chat, Anda dapat menampilkan jumlah pesan yang belum dibaca di tab chat.

Untuk menambahkan badge ke tab, Anda dapat menggunakan constructor 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),
        ),
    });

Pada contoh di atas, kita menambahkan badge ke tab Chat dengan jumlah awal 10.

Anda juga dapat memperbarui jumlah badge secara programatis.

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

Secara default, jumlah badge akan diingat. Jika Anda ingin menghapus jumlah badge setiap sesi, Anda dapat mengatur rememberCount ke false.

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

Menambahkan Alert ke Tab

Anda dapat menambahkan alert ke tab Anda.

Terkadang Anda mungkin tidak ingin menampilkan jumlah badge, tetapi ingin menampilkan indikator alert kepada pengguna.

Untuk menambahkan alert ke tab, Anda dapat menggunakan constructor 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),
        ),
    });

Ini akan menambahkan alert ke tab Chat dengan warna merah.

Anda juga dapat memperbarui alert secara programatis.

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

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

Indeks Awal

Secara default, Navigation Hub dimulai dari tab pertama (indeks 0). Anda dapat mengubahnya dengan meng-override getter initialIndex.

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

Mempertahankan State

Secara default, state Navigation Hub dipertahankan.

Ini berarti saat Anda berpindah ke tab, state tab tersebut akan dipertahankan.

Jika Anda ingin menghapus state tab setiap kali berpindah ke sana, Anda dapat mengatur maintainState ke false.

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

onTap

Anda dapat meng-override method onTap untuk menambahkan logika kustom saat sebuah tab diketuk.

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

Aksi State

Aksi state adalah cara untuk berinteraksi dengan Navigation Hub dari mana saja di aplikasi Anda.

Berikut aksi state yang dapat Anda gunakan:

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

Untuk menggunakan aksi state, Anda dapat melakukan hal berikut:

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

Gaya Loading

Secara default, Navigation Hub akan menampilkan Widget loading default Anda (resources/widgets/loader_widget.dart) saat tab sedang loading.

Anda dapat menyesuaikan loadingStyle untuk memperbarui gaya loading.

Gaya Deskripsi
normal Gaya loading default
skeletonizer Gaya loading skeleton
none Tanpa gaya loading

Anda dapat mengubah gaya loading seperti ini:

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

Jika Anda ingin memperbarui Widget loading di salah satu gaya, Anda dapat memasukkan child ke LoadingStyle.

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

Sekarang, saat tab sedang loading, teks "Loading..." akan ditampilkan.

Contoh di bawah ini:

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

Membuat Navigation Hub

Untuk membuat Navigation Hub, Anda dapat menggunakan Metro, gunakan perintah di bawah ini.

metro make:navigation_hub base

Perintah ini akan memandu Anda melalui pengaturan interaktif di mana Anda dapat memilih tipe layout dan menentukan tab atau journey state Anda.

Ini akan membuat file base_navigation_hub.dart di direktori resources/pages/navigation_hubs/base/ Anda dengan widget anak yang terorganisir di subfolder tabs/ atau states/.