In this example, each tab has its own navigation stack. This is so we don’t lose the navigation history when switching tabs.
This is a very common use case for a lot of apps.

How is it built?
– Create an app with a Scaffold and a BottomNavigationBar.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _buildBody(),
      bottomNavigationBar: BottomNavigation(
        currentTab: _currentTab,
        onSelectTab: _selectTab,
      ),
    );
  }

– In the Scaffold body, create a Stack with one child for each tab, and don’t forget to handle Android back navigation with WillPopScope.

@override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        final isFirstRouteInCurrentTab =
            !await _navigatorKeys[_currentTab]!.currentState!.maybePop();
        if (isFirstRouteInCurrentTab) {
          // if not on the 'main' tab
          if (_currentTab != TabItem.red) {
            // select 'main' tab
            _selectTab(TabItem.red);
            // back button handled by app
            return false;
          }
        }
        // let system handle back button if we're on the first route
        return isFirstRouteInCurrentTab;
      },
      child: Scaffold(
        body: Stack(children: <Widget>[
          _buildOffstageNavigator(TabItem.red),
          _buildOffstageNavigator(TabItem.green),
          _buildOffstageNavigator(TabItem.blue),
        ]),
        bottomNavigationBar: BottomNavigation(
          currentTab: _currentTab,
          onSelectTab: _selectTab,
        ),
      ),
    );
  }

– Create each child with an Offstage widget with a child Navigator.

final navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TabNavigator(
        navigatorKey: navigatorKey,
        tabItem: currentTab,
      ),
      bottomNavigationBar: BottomNavigation(
        currentTab: currentTab,
        onSelectTab: _selectTab,
      ),
    );
  }

## How does the _push() method work?

MaterialPageRoute takes care of creating a new route to be pushed
Navigator.of(context) finds a Navigator above in the widget tree and uses it to push the new route.
You may wonder, where does the Navigator widget come from?
We haven’t created one ourselves and the parent of our App class is the MaterialApp at the root of the widget tree.
As it turns out, MaterialApp creates its own Navigator internally.
However, if we just use Navigator.of(context) to push the new route, something unexpected happens.
The whole BottomNavigationBar and its contents slide away as the new page is presented.

## Ok Navigator, show me what you can do?

The solution is to wrap the body of our Scaffold object with a new Navigator.
How does this work?

In step 1, we define two route names: / and /detail.

In step 2, we define the constructor for TabNavigator. This takes a navigatorKey and a tabItem.

Note that navigatorKey has the type GlobalKey. We need this to uniquely identify the navigator across the entire app (read more about GlobalKey here).

In step 3, we define a _routeBuilders method, which associates a WidgetBuilder to each of the two routes we have defined. We’ll look at the ColorsListPage and ColorDetailPage in a second.

In step 4, we implement the build() method, which returns a new Navigator object. This takes a key and an initialRoute parameter.

It also has an onGenerateRoute method, which is called every time a route needs to be generated. This uses the _routeBuilders() method we have defined above.

In step 5, we define a _push() method used to push a detailed route with a ColorDetailPage.

// 1

class TabNavigatorRoutes {
  static const String root = '/';
  static const String detail = '/detail';
}

// 2

class TabNavigator extends StatelessWidget {
  TabNavigator({this.navigatorKey, this.tabItem});
  final GlobalKey<NavigatorState> navigatorKey;
  final TabItem tabItem;

// 3

Map<String, WidgetBuilder> _routeBuilders(BuildContext context,
    {int materialIndex: 500}) {
  return {
    TabNavigatorRoutes.root: (context) => ColorsListPage(
          color: TabHelper.color(tabItem),
          title: TabHelper.description(tabItem),
          onPush: (materialIndex) =>
              _push(context, materialIndex: materialIndex),
        ),
    TabNavigatorRoutes.detail: (context) => ColorDetailPage(
          color: TabHelper.color(tabItem),
          title: TabHelper.description(tabItem),
          materialIndex: materialIndex,
        ),
  };
}

// 4

@override
  Widget build(BuildContext context) {
    final routeBuilders = _routeBuilders(context);
    return Navigator(
      key: navigatorKey,
      initialRoute: TabNavigatorRoutes.root,
      onGenerateRoute: (routeSettings) {
        return MaterialPageRoute(
          builder: (context) => routeBuilders[routeSettings.name](context),
        );
      },
    );
  }

// 5

void _push(BuildContext context, {int materialIndex: 500}) {
    var routeBuilders = _routeBuilders(context, materialIndex: materialIndex);

    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => routeBuilders[TabNavigatorRoutes.detail](context),
      ),
    );
  }
}

This is a long code, so the code is in the link.

Support On Demand!

Flutter