flutter
/

Flutter BottomNavigationBar – Complete Guide with Examples

Last Sync: Today

On this page

8
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Flutter BottomNavigationBar – Complete Guide with Examples

What is BottomNavigationBar?

BottomNavigationBar is a widget that displays a row of icons and labels at the bottom of the screen. It's commonly used for navigating between the top‑level views of an app (e.g., home, search, profile). The selected item is visually highlighted, and when the user taps an item, the app updates the displayed content. This widget is a fundamental part of Material Design and provides a consistent navigation experience on mobile.

Basic Implementation

The simplest use involves a StatefulWidget that holds the current selected index. You provide a list of BottomNavigationBarItem widgets to the items property, and set currentIndex to the selected one. The onTap callback updates the index and (usually) the content.

DARTRead-only
1
class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 0;

  static const List<Widget> _pages = [
    HomePage(),
    SearchPage(),
    ProfilePage(),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('BottomNavigationBar')),
      body: _pages[_selectedIndex],
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
        ],
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
    );
  }
}

Customizing Appearance

BottomNavigationBar offers several properties to control its look:

    • type: BottomNavigationBarType.fixed (all items visible) or shifting (selected item expands; usually for 3+ items).
    • selectedItemColor: Color of the selected icon and label.
    • unselectedItemColor: Color of unselected items.
    • backgroundColor: Background color of the bar.
    • elevation: Elevation (shadow) of the bar.
    • showSelectedLabels and showUnselectedLabels: Control label visibility.
    • iconSize: Size of the icons.
DARTRead-only
1
BottomNavigationBar(
  type: BottomNavigationBarType.fixed,
  selectedItemColor: Colors.blue,
  unselectedItemColor: Colors.grey,
  backgroundColor: Colors.white,
  elevation: 8,
  showSelectedLabels: true,
  showUnselectedLabels: false,
  iconSize: 28,
  // ... items, currentIndex, onTap
)

Handling Back Navigation with IndexedStack

When using multiple pages, each page might have its own navigation stack. A common approach is to use an IndexedStack to keep all pages alive, so their navigation state is preserved when switching tabs. The IndexedStack displays only the selected index but keeps all children in memory.

DARTRead-only
1
body: IndexedStack(
  index: _selectedIndex,
  children: _pages,
)

Combining with Routes (Navigator)

For more complex navigation where each tab has its own navigation stack, you can embed a Navigator inside each page. This allows per‑tab navigation history. A common pattern is to use a TabNavigator widget that holds a Navigator for that tab.

DARTRead-only
1
class TabNavigator extends StatelessWidget {
  final String tabName;
  final Widget child;

  TabNavigator({required this.tabName, required this.child});

  @override
  Widget build(BuildContext context) {
    return Navigator(
      initialRoute: '/',
      onGenerateRoute: (settings) {
        if (settings.name == '/') {
          return MaterialPageRoute(builder: (_) => child);
        }
        // handle other routes within the tab
        return null;
      },
    );
  }
}

Common Mistakes

    • Forgetting to call setState: The bar won't update the selected index without calling setState in onTap.
    • Using too many items: For 4+ items, consider using type: BottomNavigationBarType.shifting for better UX.
    • Not handling back navigation: When the user presses the back button, you may want to exit the app or navigate to the previous screen inside the tab. Using IndexedStack with WillPopScope can help.
    • Changing the list of items after building: The list of items should be static; changing it requires a new BottomNavigationBar to be built.
    • Over‑customizing: Making the bar too tall or using large icons can break the Material Design guidelines.

Best Practices

    • Keep the number of items between 3 and 5. For 5+ items, consider alternatives like a drawer or tab bar.
    • Provide both an icon and a label for each item to aid accessibility.
    • Use IndexedStack to preserve page state if needed.
    • For deep navigation within a tab, use separate Navigator instances per tab.
    • Test on different screen sizes; on tablets, you might prefer a navigation rail.

Key Takeaways

    • BottomNavigationBar is used for top‑level navigation with icons and labels.
    • The widget is typically placed inside Scaffold's bottomNavigationBar property.
    • Use a StatefulWidget with currentIndex and onTap to update the selected page.
    • Customize colors, icon size, and behavior with properties like selectedItemColor, type, etc.
    • Use IndexedStack to keep pages alive when switching tabs.
    • For per‑tab navigation history, consider using separate Navigator instances.

Try it yourself

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BottomNav Demo',
      home: BottomNavDemo(),
    );
  }
}

class BottomNavDemo extends StatefulWidget {
  @override
  _BottomNavDemoState createState() => _BottomNavDemoState();
}

class _BottomNavDemoState extends State<BottomNavDemo> {
  int _currentIndex = 0;

  final List<Widget> _pages = [
    Center(child: Text('Home Page', style: TextStyle(fontSize: 24))),
    Center(child: Text('Search Page', style: TextStyle(fontSize: 24))),
    Center(child: Text('Profile Page', style: TextStyle(fontSize: 24))),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('BottomNavigationBar')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) => setState(() => _currentIndex = index),
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
        ],
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.grey,
        type: BottomNavigationBarType.fixed,
      ),
    );
  }
}

Test Your Knowledge

Q1
of 4

Which property of Scaffold is used to add a bottom navigation bar?

A
bottom
B
bottomNavigationBar
C
navigationBar
D
footer
Q2
of 4

How do you update the selected tab when the user taps a different item?

A
Set the `currentIndex` property directly
B
Call `setState` inside the `onTap` callback to update the index
C
Use a `StreamBuilder`
D
It updates automatically
Q3
of 4

Which widget can you use to keep all pages alive so their state is preserved when switching tabs?

A
PageView
B
IndexedStack
C
Stack
D
Container
Q4
of 4

What is the recommended maximum number of items in a bottom navigation bar?

A
3
B
5
C
7
D
No limit

Frequently Asked Questions

How do I change the background color of the selected item?

Use selectedItemColor to change the icon and label color. To change the background behind the selected item, you would need to use the type: BottomNavigationBarType.shifting and provide a custom backgroundColor for each item? Actually, shifting style animates the background behind the selected item; you can control the color using selectedItemColor (the ripple effect). For a solid background, you might need to implement a custom widget.

Can I have a BottomNavigationBar without labels?

Yes, set showSelectedLabels: false and showUnselectedLabels: false. The bar will show only icons. However, for accessibility, consider providing labels via tooltips or semantic labels.

How do I handle the back button to exit the app from the first tab?

Wrap your Scaffold with a WillPopScope and override onWillPop. Check the current index; if it's 0, allow exit (or show a confirmation). Otherwise, set the index to 0 and return false.

What's the difference between `fixed` and `shifting` type?

fixed shows all items with the same size; suitable for 3‑4 items. shifting gives more space to the selected item, animating its icon and label; it's ideal for 4‑5 items and provides a richer visual effect.

How do I add a badge or notification indicator to a tab?

You can use BottomNavigationBarItem's icon as a Stack containing the icon and a Positioned badge. For example: icon: Stack(children: [Icon(Icons.message), Positioned(... Badge())]).

Previous

flutter navigation arguments

Next

flutter drawer widget

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.