Flutter - Custom Bottom Navigation Bar - GeeksforGeeks

06 Dec, 2021
A bottom navigation bar is a material widget that is present at the bottom of an app for selecting or navigating to different pages of the app. It is usually used in conjunction with a Scaffold, where it is provided as the Scaffold.bottomNavigationBar argument.
Though flutter provides you with the BottomNavigationBar class, in this article you will learn how to create your own bottom navigation bar. This would be an in-depth tutorial.
As you can see from the implementation of the bottom navigation property the bottomNavigationBar does not specifically mention a widget. This gives us the flexibility to assign our widget of choice to the bottomNavigationBar attribute of the Scaffold.
Let’s get started.
Once you get your basic flutter app up and running, your material app assigns the HomePage() class to the home attribute.
Dart
import 'package:flutter/material.dart';  void main() {  runApp(const MyApp());}  class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);    // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Bottom NavBar Demo',      debugShowCheckedModeBanner: false,      home: HomePage(),    );  }}
Now create a stateful widget and name it as HomePage. Return a Scaffold from the HomePage class. Now this Scaffold is the main element that contains our bottom navigation bar.
Dart
class HomePage extends StatefulWidget {  const HomePage({Key? key}) : super(key: key);    @override  _HomePageState createState() => _HomePageState();}  class _HomePageState extends State<HomePage> {      @override  Widget build(BuildContext context) {    return Scaffold();  }}
Before starting to create our bottom navigation bar, create 2-5 pages. Always try to keep your bottom navigation bar minimal with at most 5 items (Pages).
Pages are the different screens of your app. In this article, we will work with 4 pages all of which are stateless widgets you can have any widget for example you can have stateful widgets, Containers, Center widgets, etc. For this tutorial, we make 4 basic stateless widgets that return a basic Page with some text.
Dart
class Page1 extends StatelessWidget {  const Page1({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 1",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}  class Page2 extends StatelessWidget {  const Page2({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 2",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}  class Page3 extends StatelessWidget {  const Page3({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 3",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}  class Page4 extends StatelessWidget {  const Page4({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 4",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}
In the HomePage class declares an int variable as pageIndex initialize it to 0. Every time, we open the app we start at the first page. You can name the variable as you wish. The pageIndex is to hold the index of your current page. Now define a final list as pages this list will hold all the pages of our app.
Add all the 4 pages that we have created.
Dart
class HomePage extends StatefulWidget {  const HomePage({Key? key}) : super(key: key);    @override  _HomePageState createState() => _HomePageState();}  class _HomePageState extends State<HomePage> {  int pageIndex = 0;    final pages = [    const Page1(),    const Page2(),    const Page3(),    const Page4(),  ];    @override  Widget build(BuildContext context) {    return Scaffold();  }}
  
Now let’s create our bottom navigation bar.
In the HomePage class let’s define the bottomNavigationBar attribute and assign a Container to it. Give it a height of 60 with some BoxDecoration (Pixels) add a Row as the child of the Container. Set the main axis alignment to space around.
Dart
class HomePage extends StatefulWidget {  const HomePage({Key? key}) : super(key: key);    @override  _HomePageState createState() => _HomePageState();}  class _HomePageState extends State<HomePage> {  int pageIndex = 0;    final pages = [    const Page1(),    const Page2(),    const Page3(),    const Page4(),  ];    @override  Widget build(BuildContext context) {    return Scaffold(      bottomNavigationBar: Container(        height: 60,        decoration: BoxDecoration(          color: Theme.of(context).primaryColor,          borderRadius: const BorderRadius.only(            topLeft: Radius.circular(20),            topRight: Radius.circular(20),          ),        ),        child: Row(          mainAxisAlignment: MainAxisAlignment.spaceAround,          children: [],        ),      ),    );  }}
For the children attribute of the Row add IconButton widgets which will be our buttons that handle the navigation of our app. Now add 4 buttons in the children list of the Row add all the required arguments. Some required arguments are Icon, onTap callback, and in order to have a clean and smooth interface, we have to handle a few elements. First up we will start by setting the enableFeedback property to false in the IconButtons. 
Dart
class HomePage extends StatefulWidget {  const HomePage({Key? key}) : super(key: key);    @override  _HomePageState createState() => _HomePageState();}  class _HomePageState extends State<HomePage> {  int pageIndex = 0;    final pages = [    const Page1(),    const Page2(),    const Page3(),    const Page4(),  ];    @override  Widget build(BuildContext context) {    return Scaffold(      bottomNavigationBar: Container(        height: 60,        decoration: BoxDecoration(          color: Theme.of(context).primaryColor,          borderRadius: const BorderRadius.only(            topLeft: Radius.circular(20),            topRight: Radius.circular(20),          ),        ),        child: Row(          mainAxisAlignment: MainAxisAlignment.spaceAround,          children: [            IconButton(              enableFeedback: false,              onPressed: () {},              icon: const Icon(                Icons.home_outlined,                color: Colors.white,                size: 35,              ),            ),            IconButton(              enableFeedback: false,              onPressed: () {},              icon: const Icon(                Icons.work_outline_outlined,                color: Colors.white,                size: 35,              ),            ),            IconButton(              enableFeedback: false,              onPressed: () {},              icon: const Icon(                Icons.widgets_outlined,                color: Colors.white,                size: 35,              ),            ),            IconButton(              enableFeedback: false,              onPressed: () {},              icon: const Icon(                Icons.person_outline,                color: Colors.white,                size: 35,              ),            ),          ],        ),      ),    );  }}
Now we will move to the (theme: ThemeData) of our material app and add the following properties. 
Dart
theme: ThemeData(    splashColor: Colors.transparent,    highlightColor: Colors.transparent,    hoverColor: Colors.transparent,  ),
But there’s a catch doing the above will apply the properties throughout the app. If you want these features to be applied to a particular widget or widget sub-tree, simply wrap your target widget with a Theme widget and provide the above data.
It looks like this:
Dart
Theme(    data: Theme.of(context).copyWith (          splashColor: Colors.transparent,          highlightColor: Colors.transparent,          hoverColor: Colors.transparent,    )    child: child,)
In short, these properties will make the user experience better compared to when these properties are set to their default values.
Coming to the implementation of the onTap property what we do now, is that we set the state of the pageIndex variable to (0, 1, 2, 3) for each IconButton respectively. This will update the body of our Scaffold every time we tap on the bottomNavigationBar items. Additionally, we add some elements like we will be changing the icons once that page is active by using the conditional operator.
Dart
class HomePage extends StatefulWidget {  const HomePage({Key? key}) : super(key: key);    @override  _HomePageState createState() => _HomePageState();}  class _HomePageState extends State<HomePage> {  int pageIndex = 0;    final pages = [    const Page1(),    const Page2(),    const Page3(),    const Page4(),  ];    @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: const Color(0xffC4DFCB),      appBar: AppBar(        leading: Icon(          Icons.menu,          color: Theme.of(context).primaryColor,        ),        title: Text(          "Geeks For Geeks",          style: TextStyle(            color: Theme.of(context).primaryColor,            fontSize: 25,            fontWeight: FontWeight.w600,          ),        ),        centerTitle: true,        backgroundColor: Colors.white,      ),      body: pages[pageIndex],      bottomNavigationBar: Container(        height: 60,        decoration: BoxDecoration(          color: Theme.of(context).primaryColor,          borderRadius: const BorderRadius.only(            topLeft: Radius.circular(20),            topRight: Radius.circular(20),          ),        ),        child: Row(          mainAxisAlignment: MainAxisAlignment.spaceAround,          children: [            IconButton(              enableFeedback: false,              onPressed: () {                setState(() {                  pageIndex = 0;                });              },              icon: pageIndex == 0                  ? const Icon(                      Icons.home_filled,                      color: Colors.white,                      size: 35,                    )                  : const Icon(                      Icons.home_outlined,                      color: Colors.white,                      size: 35,                    ),            ),            IconButton(              enableFeedback: false,              onPressed: () {                setState(() {                  pageIndex = 1;                });              },              icon: pageIndex == 1                  ? const Icon(                      Icons.work_rounded,                      color: Colors.white,                      size: 35,                    )                  : const Icon(                      Icons.work_outline_outlined,                      color: Colors.white,                      size: 35,                    ),            ),            IconButton(              enableFeedback: false,              onPressed: () {                setState(() {                  pageIndex = 2;                });              },              icon: pageIndex == 2                  ? const Icon(                      Icons.widgets_rounded,                      color: Colors.white,                      size: 35,                    )                  : const Icon(                      Icons.widgets_outlined,                      color: Colors.white,                      size: 35,                    ),            ),            IconButton(              enableFeedback: false,              onPressed: () {                setState(() {                  pageIndex = 3;                });              },              icon: pageIndex == 3                  ? const Icon(                      Icons.person,                      color: Colors.white,                      size: 35,                    )                  : const Icon(                      Icons.person_outline,                      color: Colors.white,                      size: 35,                    ),            ),          ],        ),      ),    );  }}
That’s it you have successfully created your own custom bottom navigation bar. You can customize the widget in any which way you want for example you can give a border-radius color, spacing, padding, etc.  
Coming to some best practices it’s always good to abstract your code. So, for this, instead of writing the whole code in the scaffold it’s self we will write a function (method) that will return our target bottom Navigation Bar widget.  
Dart
Container buildMyNavBar(BuildContext context) {    return Container(      height: 60,      decoration: BoxDecoration(        color: Theme.of(context).primaryColor,        borderRadius: const BorderRadius.only(          topLeft: Radius.circular(20),          topRight: Radius.circular(20),        ),      ),      child: Row(        mainAxisAlignment: MainAxisAlignment.spaceAround,        children: [          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 0;              });            },            icon: pageIndex == 0                ? const Icon(                    Icons.home_filled,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.home_outlined,                    color: Colors.white,                    size: 35,                  ),          ),          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 1;              });            },            icon: pageIndex == 1                ? const Icon(                    Icons.work_rounded,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.work_outline_outlined,                    color: Colors.white,                    size: 35,                  ),          ),          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 2;              });            },            icon: pageIndex == 2                ? const Icon(                    Icons.widgets_rounded,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.widgets_outlined,                    color: Colors.white,                    size: 35,                  ),          ),          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 3;              });            },            icon: pageIndex == 3                ? const Icon(                    Icons.person,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.person_outline,                    color: Colors.white,                    size: 35,                  ),          ),        ],      ),    );  }
The complete source code is given below with some additional elements like colors etc.
Dart
import 'package:flutter/material.dart';  void main() {  runApp(const MyApp());}  class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);    // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Bottom NavBar Demo',      theme: ThemeData(        primaryColor: const Color(0xff2F8D46),        splashColor: Colors.transparent,        highlightColor: Colors.transparent,        hoverColor: Colors.transparent,      ),      debugShowCheckedModeBanner: false,      home: const HomePage(),    );  }}  class HomePage extends StatefulWidget {  const HomePage({Key? key}) : super(key: key);    @override  _HomePageState createState() => _HomePageState();}  class _HomePageState extends State<HomePage> {  int pageIndex = 0;    final pages = [    const Page1(),    const Page2(),    const Page3(),    const Page4(),  ];    @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: const Color(0xffC4DFCB),      appBar: AppBar(        leading: Icon(          Icons.menu,          color: Theme.of(context).primaryColor,        ),        title: Text(          "Geeks For Geeks",          style: TextStyle(            color: Theme.of(context).primaryColor,            fontSize: 25,            fontWeight: FontWeight.w600,          ),        ),        centerTitle: true,        backgroundColor: Colors.white,      ),      body: pages[pageIndex],      bottomNavigationBar: buildMyNavBar(context),    );  }    Container buildMyNavBar(BuildContext context) {    return Container(      height: 60,      decoration: BoxDecoration(        color: Theme.of(context).primaryColor,        borderRadius: const BorderRadius.only(          topLeft: Radius.circular(20),          topRight: Radius.circular(20),        ),      ),      child: Row(        mainAxisAlignment: MainAxisAlignment.spaceAround,        children: [          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 0;              });            },            icon: pageIndex == 0                ? const Icon(                    Icons.home_filled,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.home_outlined,                    color: Colors.white,                    size: 35,                  ),          ),          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 1;              });            },            icon: pageIndex == 1                ? const Icon(                    Icons.work_rounded,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.work_outline_outlined,                    color: Colors.white,                    size: 35,                  ),          ),          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 2;              });            },            icon: pageIndex == 2                ? const Icon(                    Icons.widgets_rounded,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.widgets_outlined,                    color: Colors.white,                    size: 35,                  ),          ),          IconButton(            enableFeedback: false,            onPressed: () {              setState(() {                pageIndex = 3;              });            },            icon: pageIndex == 3                ? const Icon(                    Icons.person,                    color: Colors.white,                    size: 35,                  )                : const Icon(                    Icons.person_outline,                    color: Colors.white,                    size: 35,                  ),          ),        ],      ),    );  }}  class Page1 extends StatelessWidget {  const Page1({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 1",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}  class Page2 extends StatelessWidget {  const Page2({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 2",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}  class Page3 extends StatelessWidget {  const Page3({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 3",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}  class Page4 extends StatelessWidget {  const Page4({Key? key}) : super(key: key);    @override  Widget build(BuildContext context) {    return Container(      color: const Color(0xffC4DFCB),      child: Center(        child: Text(          "Page Number 4",          style: TextStyle(            color: Colors.green[900],            fontSize: 45,            fontWeight: FontWeight.w500,          ),        ),      ),    );  }}
Output:
Flutter UI-components
Android
Dart
Flutter
Android
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Broadcast Receiver in Android With Example
How to Create and Add Data to SQLite Database in Android?
Content Providers in Android with Example
Android RecyclerView in Kotlin
Navigation Drawer in Android
Flutter - DropDownButton Widget
Getter and Setter Methods in Dart
Dart - Standard Input Output
Flutter - BoxShadow Widget
ListView Class in Flutter