How To Use BottomNavigationBar In Flutter Apps

Introduction Link to heading

Mobile apps often use bottom navigation. You can reach all entries with one hand and it’s the same for Android and iOS. For Flutter apps, there is the widget BottomNavigationBar to accomplish this. In this article, I’ll share how to use BottomNavigationBar in Flutter apps.

We start with this standard implementation:

Dart

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepPurple,
        ),
      ),
      home: const MainPage(),
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  final List<String> pages = ["Home", "Search", "Settings"];
  String _selectedPage = "Home";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: pages.indexOf(_selectedPage),
        onTap:
            (index) => setState(() {
              _selectedPage = pages[index];
            }),
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: pages[0]),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: pages[1]),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: pages[2]),
        ],
      ),
      body: Center(child: Text(_selectedPage)),
    );
  }
}

There are 3 item that set the page to the corresponding entry when they are clicked. Every item consists of an icon and a label.

And it looks like this

BottomNavigationBar in a Flutter app

BottomNavigationBar in a Flutter app

You can already see that the selected item is slightly bigger than the others. Let’s start changing the style.

Change item colors Link to heading

First, we start changing the colors. By default, the primary color is used to highlight the selected entry. To set another color try this

Dart

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(
        seedColor: Colors.deepPurple,
      ),
      bottomNavigationBarTheme: BottomNavigationBarThemeData(
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.blueGrey,
      ),
    ),
    home: const MainPage(),
  );
}

You can either set the colors for the entire items or use selectedIconTheme/ unselectedIconTheme and selectedLabelStyle/unselectedLabelStyle to have even more control.

BottomNavigationBar with non-standard colors for selected and unselected items

BottomNavigationBar with non-standard colors for selected and unselected items

Make items have the same size Link to heading

Let’s make all icons and texts always have the same size.

Dart

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(
        seedColor: Colors.deepPurple,
      ),
      bottomNavigationBarTheme: BottomNavigationBarThemeData(
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.blueGrey,
        selectedLabelStyle: TextStyle(fontSize: 16),
        unselectedLabelStyle: TextStyle(fontSize: 16),
        selectedIconTheme: IconThemeData(size: 24),
        unselectedIconTheme: IconThemeData(size: 24),
      ),
    ),
    home: const MainPage(),
  );
}

In the app, all texts and icons have the same size now. It doesn’t matter if an entry is selected or not.

BottomNavigationBar with equally sized items

BottomNavigationBar with equally sized items

Hide labels Link to heading

You can also hide the labels and make the icons bigger. Try this

Dart

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      bottomNavigationBarTheme: BottomNavigationBarThemeData(
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.blueGrey,
        selectedLabelStyle: TextStyle(fontSize: 16),
        unselectedLabelStyle: TextStyle(fontSize: 16),
        selectedIconTheme: IconThemeData(size: 32),
        unselectedIconTheme: IconThemeData(size: 32),
        showSelectedLabels: false,
        showUnselectedLabels: false,
      ),
    ),
    home: const MainPage(),
  );
}

Possible, but I personally don’t prefer this approach. Icons only are usually not that good to identify the purpose of an item.

BottomNavigationBar with hidden labels

BottomNavigationBar with hidden labels

Highlight the selected item Link to heading

By default, the selected item was a bit larger than the others. But there is another option: BottomNavigationBarType. Here is how it works:

Dart

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      bottomNavigationBarTheme: BottomNavigationBarThemeData(
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.blueGrey,
        selectedLabelStyle: TextStyle(fontSize: 16),
        unselectedLabelStyle: TextStyle(fontSize: 16),
        selectedIconTheme: IconThemeData(size: 32),
        unselectedIconTheme: IconThemeData(size: 32),
        type: BottomNavigationBarType.shifting,
      ),
    ),
    home: const MainPage(),
  );
}

The default value is fixed. When set to shifting, the selected item grabs a little more space than the other and the label is automatically hidden if you don’t explicitly set showUnselectedLabels to true.

Here is how it looks. I personally don’t like it that way.

BottomNavigationBar with enlarged selected item

BottomNavigationBar with enlarged selected item

Change elevation Link to heading

Did you notice the fine line above the navigation bar?

It’s there because the widget is elevated. You can increase the value to make the gradient (it’s not a line but rather a gradient) larger or set it to 0 to have a clean area.

Dart

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      bottomNavigationBarTheme: BottomNavigationBarThemeData(
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.blueGrey,
        selectedLabelStyle: TextStyle(fontSize: 16),
        unselectedLabelStyle: TextStyle(fontSize: 16),
        selectedIconTheme: IconThemeData(size: 32),
        unselectedIconTheme: IconThemeData(size: 32),
        elevation: 0
      ),
    ),
    home: const MainPage(),
  );
}

It depends on what you like more!

BottomNavigationBar without any elevation

BottomNavigationBar without any elevation

Change background color Link to heading

Of course, you can change the background color if you like. Have a look:

Dart

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      bottomNavigationBarTheme: BottomNavigationBarThemeData(
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.blueGrey,
        selectedLabelStyle: TextStyle(fontSize: 16),
        unselectedLabelStyle: TextStyle(fontSize: 16),
        selectedIconTheme: IconThemeData(size: 32),
        unselectedIconTheme: IconThemeData(size: 32),
        elevation: 0
        backgroundColor: Colors.amber
      ),
    ),
    home: const MainPage(),
  );
}

💡 Tip

Note that this does not work if the type is set to shifting! The color is then ignored.

BottomNavigationBar with different background color

BottomNavigationBar with different background color

Testing Link to heading

When writing widget tests for your app, be aware that you cannot find the type BottomNavigationBarItem. Here is an example:

In widget tests, BottomNavigationBarItems cannot be found and the test fails.

In widget tests, BottomNavigationBarItems cannot be found and the test fails.

As a workaround, you can check for the labels with find.text or icons with find.byIcon of your entries. Another approach is to assign a Key to every item and check for the key with find.byKey.

Conclusion Link to heading

These are the basics about how to use BottomNavigationBar in Flutter apps. Testing is a bit tricky but apart from that, it’s an easy to use and common widget in mobile apps.

How To Use The Flutter Badge Widget Link to heading

Here is how to use the Flutter Badge widget that offers an easy way to add tags to your existing UI. Notification counters, shopping item tags, or list index numbers are only some of the many use cases of this widget.

How To Use The Scaffold Widget In A Flutter Application Link to heading

Here is how to use the Scaffold widget in a Flutter application to create a consistent app skeleton in no time. It’s the most common basic building block in Flutter apps and here is how it works!

How To Create A New Flutter Project Link to heading

This short article shows how to create a new Flutter project in 3 different ways, so you can start working on your app. Here is how to do it with Visual Studio Code, Android Studio, and the command line.