After searching google for examples of Flutter navigation, more specifically the bottom navigation bar I was a bit stumped. Several of the examples I found would show the bottom navigation bar on one page but not persist it across all pages, and that wasn’t what I was after. Eventually I stumbled upon this excellent article by Andrea Bizzotto not only showing me how to persist the bottom navigation bar on every page, but also how to combine this with a stack navigator on each page.
Table of contents
The bottom navigation example
After working through his bottom navigation example and getting it working for me locally, I started to try and work out how to adapt this to build an app I had in mind. It would need a :
- bottom navigation bar,
- a stack navigator on at least one of those pages,
- but then a single page on at least one of the others.
So I adapted Andrea’s code to do just that and you can find it here on github.
I’ve outlined the code changes I had to make step by step so that you can understand the process I went through.
Building on the navigation example code
I started by forking his repo but found it wouldn’t run and I had to add this to pubspec.yaml:
environment: sdk: ">=2.7.0 <3.0.0"
In order to update this code to handle some pages with a stack and some as a standalone page I made the following changes.
Combine a Flutter tab navigator with the bottom navigation bar
First of all a new property needed to be added to the TabNavigator class in tab_navigator.dart, so that as well as the navigatorKey and the tabItem properties, there's a new one called stacked, which is a boolean. So the top of the TabNavigator class now looked like this:
class TabNavigator extends StatelessWidget { TabNavigator({this.navigatorKey, this.tabItem, this.stacked}); final GlobalKey<NavigatorState> navigatorKey; final TabItem tabItem; final bool stacked; void _push(BuildContext context, {int materialIndex: 500}) { var routeBuilders = _routeBuilders(context, materialIndex: materialIndex); .... ....
The build method in tab_navigator.dart also needed to be updated so that if a page is a 'stacked' page, the routeBuilders method is still called, but if not, the SinglePage is returned.
@override Widget build(BuildContext context) { final routeBuilders = _routeBuilders(context); return Navigator( key: navigatorKey, initialRoute: TabNavigatorRoutes.root, onGenerateRoute: (routeSettings) { if (stacked) { return MaterialPageRoute( builder: (context) { return routeBuilders[routeSettings.name](context); }, ); } return MaterialPageRoute( builder: (context) => SinglePage( color: activeTabColor[tabItem], title: tabName[tabItem], materialIndex: 0, ), ); }); }
Add new file single_page.dart containing this code:
import 'package:flutter/material.dart'; class SinglePage extends StatelessWidget { SinglePage({this.color, this.title, this.materialIndex: 500}); final MaterialColor color; final String title; final int materialIndex; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: color, title: Text( '$title[$materialIndex]', ), ), body: Container( color: color[materialIndex], child: Center( child: Text( "I am a single page.", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 28), )), ), ); } }
Finally add a reference to that page in tab_navigator.dart:
import 'package:nested_navigation_demo_flutter/single_page.dart';
Identify single and stacked pages
Next I needed to modify app.dart, to identify which page would be a single page, and which would be stacked.
Add new property to the _buildOffstageNavigator method, so that we can pass in whether each page has a stack or not.
Widget _buildOffstageNavigator(TabItem tabItem, bool isStacked) { return Offstage( offstage: _currentTab != tabItem, child: TabNavigator( navigatorKey: _navigatorKeys[tabItem], tabItem: tabItem, stacked: isStacked, ), ); }
Once that change had been made I had to change the code where the _buildOffstageNavigator method is called to include the new boolean property. This is in the build method of Appstate.dart -
.... child: Scaffold( body: Stack(children: <Widget>[ _buildOffstageNavigator(TabItem.red, true), _buildOffstageNavigator(TabItem.green, false), _buildOffstageNavigator(TabItem.blue, true), ]), bottomNavigationBar: BottomNavigation( currentTab: _currentTab, onSelectTab: _selectTab, ), ), .......
Watch the app demo
Once all of those changes are made you should have an app that works like you can see in this video, with a Flutter bottom navigation bar on every page, with two of the main pages showing stack navigation and one just a single page.
So that's a fully working Flutter bottom navigation bar example. You may also be interested in a blog post I wrote recently about my experience installing and setting up Flutter on my PC, be sure to read it if you are just starting out.