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.