How to create a material 3 bottom navigation bar in Flutter
Flutter is a UI development kit created by Google. It uses the Dart programming language to construct hybrid or cross-platform apps.
Material design is a flexible and adaptable methodology that helps create excellent digital experiences.
To create the material design of 3 types of bottom navigation bars in Flutter, we only need to use the NavigationBar class and track the changes of the currently selected tab on our navigation bar.
Note: We must be using Flutter version 3.0 or higher to follow this tutorial because this version gives us access to Material 3 widgets.
What is the NavigationBar?
The NavigationBar widget is used as the equivalent of the BottomNavigationBar widget, which conforms more to material design 2. With that being said, letβs see how to create this navigation bar.
First, create a new flutter project and replace everything in the main.dart with the code below.
import 'package:flutter/material.dart';void main() {runApp(const MyApp());}class MyApp extends StatelessWidget {const MyApp({super.key});// This widget is the root of your application.@overrideWidget build(BuildContext context) {return const MaterialApp(title: 'Buttom Navigation Bar',home: Scaffold(body: Center(child: Text('Hello World π',),),),);}}
Explanation
Line 4: We run the
MyAppclass in therunAppmethod, which is responsible for displayingMyAppwidget on the screen.Lines 7β13: In the
MyAppclass, we return theMaterialAppwidget to give us access to many other widgets, which are created to make building our UIs easier.Line 15: Under the
Scaffoldwidget, we have a property calledhomewhich serves as the default route of the app when we open the app.Lines 16β24: The
Scaffoldwidget gives us access to quite a number of valuable properties such as app-bar, body, drawer, bottom navigation bar, and some other properties. We'll be using only two properties: the body and the bottom navigation bar property.
Also, we need to add the bottomNavigationBar property provided by the Scaffold widget and pass in the BottomNavigationBar widget or NavigationBar widget. For this example, we'll use the NavigationBar widget because it is best suited to achieve our goal.
import 'package:flutter/material.dart';void main() {runApp(const MyApp());}class MyApp extends StatelessWidget {const MyApp({super.key});// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Buttom Navigation Bar',home: Scaffold(body: const Center(child: Text('Hello World π',),),bottomNavigationBar: NavigationBar(onDestinationSelected: (int index) {print('Selected $index');},selectedIndex: 0,destinations: const <NavigationDestination>[NavigationDestination(selectedIcon: Icon(Icons.person),icon: Icon(Icons.person_outline),label: 'Learn',),NavigationDestination(selectedIcon: Icon(Icons.engineering),icon: Icon(Icons.engineering_outlined),label: 'Relearn',),NavigationDestination(selectedIcon: Icon(Icons.bookmark),icon: Icon(Icons.bookmark_border),label: 'Unlearn',),],),),);}}
Explanation
Lines 21β43: We assign the
NavigationBarwidget to thebottomNavigationBarproperty.
The NavigationBar widget
The NavigationBar widget gives us access to a few widgets such as the following:
onDestinationSelected: TheonDestinationSelectedproperty takes a function that takes anintas an argument. The function is called when a user taps on a navigation bar item (NavigationDestination). Theintargument represents the index of the selected item.selectedIndex: TheseletedIndexproperty determines which of the navigation bar items (NavigationDestination) is selected.destinations: The list of widgets (NavigationDestination) that will be displayed in ourNavigationBarwidget is passed to the Navigation widget through thedestinationsproperty. TheNavigationDestinationrequires us to pass an icon and label properties as a required property/parameter.
Displaying selected navigation item
We need to convert our MyApp class from a StatelessWidget to a StatefulWidget (because we need to rebuild some parts of the widget). To do this (either on VS Code or Android Studio), double-tap on the stateless keyword and click the "lightbulb" icon that appears on the left or right side of the editor. An option telling us to convert our widget to a StatefulWidget is shown.
Next, we want to create a new variable called selectedPageIndex and assign it an integer. Here we'll assign 0 as the starting value. Next, pass the selectedPageIndex to the selectedIndex property in the NavigationBar widget.
Then, we need to make the selectedPageIndex update based on the selected navigation bar item (NavigationDestination). We'll achieve this by assigning the selectedPageIndex to the index of the selected page.
import 'package:flutter/material.dart';void main() {runApp(const MyApp());}class MyApp extends StatefulWidget {const MyApp({super.key});@overrideState<MyApp> createState() => _MyAppState();}class _MyAppState extends State<MyApp> {int selectedPageIndex = 0;@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Buttom Navigation Bar',home: Scaffold(body: const [Center(child: Text('Learn π',),),Center(child: Text('Relearn π¨βπ«',),),Center(child: Text('Unlearn π',),),],bottomNavigationBar: NavigationBar(selectedIndex: selectedPageIndex,onDestinationSelected: (int index) {setState(() {selectedPageIndex = index;});},destinations: const <NavigationDestination>[NavigationDestination(selectedIcon: Icon(Icons.person),icon: Icon(Icons.person_outline),label: 'Learn',),NavigationDestination(selectedIcon: Icon(Icons.engineering),icon: Icon(Icons.engineering_outlined),label: 'Relearn',),NavigationDestination(selectedIcon: Icon(Icons.bookmark),icon: Icon(Icons.bookmark_border),label: 'Unlearn',),],),),);}}
Explanation
Line 15: We create a new variable to hold the selected page index.
Line 39: We pass the
selectedPageIndexvariable to theselectedIndexproperty.Lines 40β44: The
setState()method tells the Flutter engine that the internal state of an object has changed, so Flutter should check and then update the UI on the device accordingly.
To make the bottom navigation bar functional, we need to add the list of widgets needed to effectively switch between tabs, and pass the index of the selected tab. This is done so that flutter knows the index of the widget that is needed to be displayed when a button is tapped.
body: [const Center(child: Text('Learn π',),),const Center(child: Text('Relearn π¨βπ«',),),const Center(child: Text('Unlearn π',),),][selectedPageIndex],
Note: The widget that needs to be displayed must match the number of navigation items (NavigationDestination) created.
Full code
Our full working code is as follows:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());}class MyApp extends StatefulWidget {const MyApp({super.key});@overrideState<MyApp> createState() => _MyAppState();}class _MyAppState extends State<MyApp> {int selectedPageIndex = 0;@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Buttom Navigation Bar',home: Scaffold(body: const [Center(child: Text('Learn π',),),Center(child: Text('Relearn π¨βπ«',),),Center(child: Text('Unlearn π',),),][selectedPageIndex],bottomNavigationBar: NavigationBar(selectedIndex: selectedPageIndex,onDestinationSelected: (int index) {setState(() {selectedPageIndex = index;});},destinations: const <NavigationDestination>[NavigationDestination(selectedIcon: Icon(Icons.person),icon: Icon(Icons.person_outline),label: 'Learn',),NavigationDestination(selectedIcon: Icon(Icons.engineering),icon: Icon(Icons.engineering_outlined),label: 'Relearn',),NavigationDestination(selectedIcon: Icon(Icons.bookmark),icon: Icon(Icons.bookmark_border),label: 'Unlearn',),],),),);}}