Bottom Navigation Bars in Flutter

Bottom Navigation Bars are a popular UI component in mobile applications, providing quick access to various app sections. They allow users to navigate between major screens efficiently and are often seen in apps like YouTube, Instagram, and Gmail. In this guide, we’ll explore how to implement and customize Bottom Navigation Bars in Flutter, step by step.

What is a Bottom Navigation Bar?

A Bottom Navigation Bar is a widget placed at the bottom of an app's screen. It typically contains multiple items, each representing a different section of the app. When a user taps on an item, the app navigates to the corresponding section.

Why Use Bottom Navigation Bars?

  1. Quick Navigation: Provides easy access to primary app sections.
  2. Compact Design: Maximizes screen space while maintaining usability.
  3. Intuitive User Experience: Familiar UI pattern for most users.
  4. Customizable: You can style and configure it to match your app's theme.

Steps to Implement a Bottom Navigation Bar

1. Basic BottomNavigationBar

The BottomNavigationBar widget is the primary tool for creating a bottom navigation bar in Flutter.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int _selectedIndex = 0;

  // List of screens corresponding to each navigation item
  static const List<Widget> _pages = <Widget>[
    Center(child: Text('Home Screen')),
    Center(child: Text('Search Screen')),
    Center(child: Text('Profile Screen')),
  ];

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bottom Navigation Example'),
      ),
      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,
        selectedItemColor: Colors.blue,
        onTap: _onItemTapped,
      ),
    );
  }
}

Understanding the Key Components

  1. items: A list of BottomNavigationBarItem objects, each defining an icon and a label. Example:

    BottomNavigationBarItem(
      icon: Icon(Icons.home),
      label: 'Home',
    )
    
  2. currentIndex: The index of the currently selected item.
  3. onTap: A callback function triggered when an item is tapped. It is used to update the currentIndex.
  4. selectedItemColor: Defines the color of the selected item.

2. Customizing the Bottom Navigation Bar

You can style and configure the Bottom Navigation Bar to match your app’s design.

bottomNavigationBar: BottomNavigationBar(
  backgroundColor: Colors.grey[900], // Change the background color
  selectedItemColor: Colors.yellow, // Selected item color
  unselectedItemColor: Colors.white, // Unselected item color
  selectedFontSize: 16.0, // Font size of the selected label
  unselectedFontSize: 14.0, // Font size of unselected labels
  type: BottomNavigationBarType.fixed, // Ensures labels are always visible
  items: [
    BottomNavigationBarItem(
      icon: Icon(Icons.dashboard),
      label: 'Dashboard',
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.settings),
      label: 'Settings',
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.notifications),
      label: 'Notifications',
    ),
  ],
)

3. Handling Dynamic Navigation with Screens

You can dynamically navigate to new screens using a Navigator or IndexedStack.

Example Using Navigator:

void _onItemTapped(int index) {
  if (index == 0) {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => HomeScreen()),
    );
  } else if (index == 1) {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => SearchScreen()),
    );
  }
}

Example Using IndexedStack:

body: IndexedStack(
  index: _selectedIndex,
  children: [
    HomeScreen(),
    SearchScreen(),
    ProfileScreen(),
  ],
),

4. Floating Bottom Navigation Bars

For a modern look, you can use a floating Bottom Navigation Bar. This can be achieved with a Container or third-party packages like salomon_bottom_bar.

bottomNavigationBar: Container(
  margin: EdgeInsets.all(16.0),
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(30),
    boxShadow: [
      BoxShadow(
        color: Colors.black12,
        blurRadius: 10,
        spreadRadius: 5,
      ),
    ],
  ),
  child: BottomNavigationBar(
    items: [
      BottomNavigationBarItem(
        icon: Icon(Icons.home),
        label: 'Home',
      ),
      BottomNavigationBarItem(
        icon: Icon(Icons.search),
        label: 'Search',
      ),
      BottomNavigationBarItem(
        icon: Icon(Icons.person),
        label: 'Profile',
      ),
    ],
    currentIndex: _selectedIndex,
    selectedItemColor: Colors.blue,
    onTap: _onItemTapped,
  ),
)

Common Errors and Solutions

  1. Navigation Index Not Updating: Ensure the _onItemTapped function is updating the currentIndex and calling setState.
  2. UI Overlaps with BottomNavigationBar: Wrap the content in SafeArea to avoid overlap with system UI.
  3. Too Many Items: BottomNavigationBar supports a maximum of five items. For more, consider using a Drawer.

Best Practices

  1. Limit Items: Use no more than 3-5 items to avoid clutter.
  2. Label Importance: Use clear and concise labels.
  3. Consistent Design: Match the navigation bar design with the app’s theme.
  4. Highlight Active Item: Always indicate which item is currently selected.

Complete Example

Here’s a complete example demonstrating a fully customized Bottom Navigation Bar with dynamic screens.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: NavigationExample(),
    );
  }
}

class NavigationExample extends StatefulWidget {
  @override
  _NavigationExampleState createState() => _NavigationExampleState();
}

class _NavigationExampleState extends State<NavigationExample> {
  int _selectedIndex = 0;

  static const List<Widget> _pages = <Widget>[
    Center(child: Text('Home Screen')),
    Center(child: Text('Search Screen')),
    Center(child: Text('Profile Screen')),
  ];

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bottom Navigation Bar Example'),
      ),
      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,
        selectedItemColor: Colors.blue,
        onTap: _onItemTapped,
      ),
    );
  }
}

Conclusion

Bottom Navigation Bars are a great way to enhance user navigation in Flutter apps. By customizing their design and behavior, you can create a smooth and intuitive user experience. Practice building your own Bottom Navigation Bars to get comfortable with their implementation and styling!