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.
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 π',),),),);}}
Line 4: We run the MyApp
class in the runApp
method, which is responsible for displaying MyApp
widget on the screen.
Lines 7β13: In the MyApp
class, we return the MaterialApp
widget to give us access to many other widgets, which are created to make building our UIs easier.
Line 15: Under the Scaffold
widget, we have a property called home
which serves as the default route of the app when we open the app.
Lines 16β24: The Scaffold
widget 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',),],),),);}}
Lines 21β43: We assign the NavigationBar
widget to the bottomNavigationBar
property.
NavigationBar
widgetThe NavigationBar
widget gives us access to a few widgets such as the following:
onDestinationSelected
: The onDestinationSelected
property takes a function that takes an int
as an argument. The function is called when a user taps on a navigation bar item (NavigationDestination
). The int
argument represents the index of the selected item.
selectedIndex
: The seletedIndex
property determines which of the navigation bar items (NavigationDestination
) is selected.
destinations
: The list of widgets (NavigationDestination
) that will be displayed in our NavigationBar
widget is passed to the Navigation widget through the destinations
property. The NavigationDestination
requires us to pass an icon and label properties as a required property/parameter.
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',),],),),);}}
Line 15: We create a new variable to hold the selected page index.
Line 39: We pass the selectedPageIndex
variable to the selectedIndex
property.
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.
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',),],),),);}}