Flutter widget for implementing multiple nested navigators with their own route stacks.

To use, you must at least specify two arguments items and generateRoute:

class RootPage extends StatefulWidget { @override State<StatefulWidget> createState() => _RootPageState(); } class _RootPageState extends State<RootPage> { @override Widget build(BuildContext context) { return NestedNavigators( items: { NestedNavItemKey.blue: NestedNavigatorItem( initialRoute: Routes.blue, icon: Icons.access_time, text: "Blue", ), NestedNavItemKey.red: NestedNavigatorItem( initialRoute: Routes.red, icon: Icons.send, text: "Red", ), NestedNavItemKey.green: NestedNavigatorItem( initialRoute: Routes.green, icon: Icons.perm_identity, text: "Green", ), }, generateRoute: Routes.generateRoute, ); } }

See Routes.generateRoute()

By default NestedNavigators displays BottomNavigationBar, but you can hide it if you want:

clearStackAfterTapOnCurrentTab: false

You can customize BottomNavigatosBar items in two ways:

configure BottomNavigationBarItem:

buildBottomNavigationItem: (key, item, selected) => BottomNavigationBarItem( icon: Icon( item.icon, color: Colors.blue, ), title: Text( item.text, style: TextStyle(fontSize: 20), ), )

Define your own widget:

buildCustomBottomNavigationItem: (key, item, selected) => Container( height: 60, child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: <Widget>[ Icon( item.icon, size: 24, color: selected ? Colors.blue : null, ), Text( item.text, style: TextStyle(fontSize: 20, color: selected ? Colors.blue : null), ), ], ), )

Initial selected navigator:

If number of navigators is even number, than it will be the first elemnt of items ;

; If number of navigators is odd number, than it will be the middle element of items ;

; You can specify your navigator key:

initialNavigatorKey: NestedNavItemKey.green

You can specify the color of ripple effect in your app theme or in bottomNavigationBarTheme:

bottomNavigationBarTheme: Theme.of(context).copyWith( splashColor: Colors.blue[100], )

By default, if a user clicks on an already selected tab item, the route stack will be cleared and the initial route will be displayed. You can disable this option:

clearStackAfterTapOnCurrentTab: false

When you navigate to another route within nested navigator, you can hide BottomNavigatorsBar for specific route, just add hideNavTabBar: true to route arguments:

Navigator.of(context).pushNamed( Routes.red, arguments: { hideNavTabBar: true, }, );

You can select another navigator from your routes:

NestedNavigatorsBlocProvider.of(context).select(NestedNavItemKey.green)

You can also switch displayed navigator and at the same time make navigation in the new navigator:

NestedNavigatorsBlocProvider.of(context).selectAndNavigate( NestedNavItemKey.green, (navigator) => navigator.pushNamed( Routes.green, arguments: { ArgumentKeys.value: 100, }, ));

Add NestedNavigatorsBlocProvider to app widget if you want to use the root navigator and need access to nested navigators from there:

class App extends StatefulWidget { @override State<StatefulWidget> createState() => _AppState(); } class _AppState extends State<App> { final NestedNavigatorsBloc _bloc = NestedNavigatorsBloc<NestedNavItemKey>(); @override Widget build(BuildContext context) { return NestedNavigatorsBlocProvider( bloc: _bloc, child: MaterialApp( title: 'Flutter Demo', home: RootPage(), onGenerateRoute: (routeSettings) => Routes.generateRoute(routeSettings), ), ); } }

You can use Drawer instead of BottomNavigationBar, use drawer or endDrawer for this:

drawer: (items, selectedItemKey, selectNavigator) => Drawer( child: ListView( children: items.entries .map((entry) => ListTile( title: Text( entry.value.text, style: TextStyle( color: entry.key == selectedItemKey ? Colors.blue : null, ), ), trailing: Icon( entry.value.icon, color: entry.key == selectedItemKey ? Colors.blue : null, ), onTap: () => selectNavigator(entry.key), )) .toList(), ), ),

Also you can open drawers from your pages: