Working with Different Navigation Patterns (Drawer, Bottom Navigation, Tabs) in Flutter

Flutter offers a variety of navigation patterns to create a smooth and intuitive user experience. Understanding how to implement different navigation patterns, such as drawers, bottom navigation bars, and tabs, is crucial for building robust and user-friendly Flutter applications. In this comprehensive guide, we will explore these patterns with detailed explanations and code examples.

Navigation Patterns in Flutter

Navigation patterns are essential UI paradigms that guide users through different parts of an application. In Flutter, popular navigation patterns include:

  • Drawer Navigation: A slide-out panel, typically on the left side of the screen, containing the app’s primary navigation.
  • Bottom Navigation: A bar at the bottom of the screen for quick access to a few top-level views or features.
  • Tabbed Navigation: Displays different sections or views via tabs, either at the top or bottom of the screen.

1. Drawer Navigation

A drawer is a side panel that slides in from the left (or right in right-to-left languages) and contains navigation options or other information. It’s perfect for apps with many destinations or less frequently accessed sections.

Implementation

To create a drawer in Flutter, use the Drawer widget within the Scaffold widget.

Step 1: Create a Scaffold with a Drawer

import 'package:flutter/material.dart';

class DrawerNavigation extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Drawer Navigation'),
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: [
            DrawerHeader(
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
              child: Text(
                'Menu',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 24,
                ),
              ),
            ),
            ListTile(
              leading: Icon(Icons.home),
              title: Text('Home'),
              onTap: () {
                Navigator.pop(context); // Close the drawer
                // Navigate to home screen
              },
            ),
            ListTile(
              leading: Icon(Icons.settings),
              title: Text('Settings'),
              onTap: () {
                Navigator.pop(context); // Close the drawer
                // Navigate to settings screen
              },
            ),
          ],
        ),
      ),
      body: Center(
        child: Text('Main Content'),
      ),
    );
  }
}
Step 2: Use the Widget

void main() {
  runApp(MaterialApp(
    home: DrawerNavigation(),
  ));
}

Explanation:

  • Scaffold is the base for the UI, containing the app bar, drawer, and body.
  • The Drawer widget holds a ListView to make the items scrollable.
  • The DrawerHeader is a special header for the drawer.
  • ListTile widgets provide tappable elements with icons and text.
  • Navigator.pop(context) is used to close the drawer when an item is tapped.

2. Bottom Navigation

A bottom navigation bar displays at the bottom of the screen, presenting 3–5 top-level destinations. It’s best suited for apps with several distinct navigation categories.

Implementation

To create a bottom navigation bar, use the BottomNavigationBar widget within the Scaffold widget.

Step 1: Create a Stateful Widget

Since we need to keep track of the selected tab, let’s create a stateful widget.


import 'package:flutter/material.dart';

class BottomNavigation extends StatefulWidget {
  @override
  _BottomNavigationState createState() => _BottomNavigationState();
}

class _BottomNavigationState extends State {
  int _selectedIndex = 0;

  static List _widgetOptions = [
    Text('Home'),
    Text('Business'),
    Text('School'),
  ];

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bottom Navigation'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'School',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}
Step 2: Use the Widget

void main() {
  runApp(MaterialApp(
    home: BottomNavigation(),
  ));
}

Explanation:

  • BottomNavigationBar requires a list of BottomNavigationBarItem widgets, each representing a tab.
  • currentIndex sets the initially selected tab and is updated using setState.
  • onTap handles the tab selection, updating the _selectedIndex.
  • The content for each tab is rendered based on the selected index using a list of widgets.

3. Tabbed Navigation

Tabbed navigation organizes content across different screens using tabs, making it easy to switch between different views within the same context. Tabs can be located at the top (TabBar) or bottom (using a BottomNavigationBar and PageView combination).

Top Tabbed Navigation

Top tabbed navigation utilizes the TabBar and TabBarView widgets.

Step 1: Create a StatefulWidget

import 'package:flutter/material.dart';

class TopTabbedNavigation extends StatefulWidget {
  @override
  _TopTabbedNavigationState createState() => _TopTabbedNavigationState();
}

class _TopTabbedNavigationState extends State
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Top Tabbed Navigation'),
        bottom: TabBar(
          controller: _tabController,
          tabs: [
            Tab(icon: Icon(Icons.home), text: 'Home'),
            Tab(icon: Icon(Icons.business), text: 'Business'),
            Tab(icon: Icon(Icons.school), text: 'School'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          Center(child: Text('Home Content')),
          Center(child: Text('Business Content')),
          Center(child: Text('School Content')),
        ],
      ),
    );
  }
}
Step 2: Use the Widget

void main() {
  runApp(MaterialApp(
    home: TopTabbedNavigation(),
  ));
}

Explanation:

  • SingleTickerProviderStateMixin is used for animating the tab transitions.
  • TabController manages the state of the tab bar and its associated views.
  • TabBar is added to the AppBar‘s bottom property to display the tabs.
  • TabBarView displays the content associated with the selected tab.

Bottom Tabbed Navigation with PageView

For bottom tabbed navigation, combine BottomNavigationBar with a PageView.

Step 1: Create a Stateful Widget

import 'package:flutter/material.dart';

class BottomTabbedNavigation extends StatefulWidget {
  @override
  _BottomTabbedNavigationState createState() => _BottomTabbedNavigationState();
}

class _BottomTabbedNavigationState extends State {
  int _selectedIndex = 0;
  final PageController _pageController = PageController();

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
    _pageController.animateToPage(
      index,
      duration: Duration(milliseconds: 300),
      curve: Curves.easeInOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bottom Tabbed Navigation'),
      ),
      body: PageView(
        controller: _pageController,
        children: [
          Center(child: Text('Home Content')),
          Center(child: Text('Business Content')),
          Center(child: Text('School Content')),
        ],
        onPageChanged: (index) {
          setState(() {
            _selectedIndex = index;
          });
        },
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'School',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}
Step 2: Use the Widget

void main() {
  runApp(MaterialApp(
    home: BottomTabbedNavigation(),
  ));
}

Explanation:

  • PageView allows you to swipe between screens. The PageController is used to programmatically change the page.
  • onPageChanged updates the selected index when the user swipes between pages.
  • animateToPage in _onItemTapped updates the PageView to the selected tab when a tab is tapped.

Conclusion

Navigation is a critical aspect of user experience. By understanding and implementing these different navigation patterns—drawers, bottom navigation, and tabs—in Flutter, you can create apps that are both intuitive and engaging. Whether you’re designing for complex applications with many features or simple apps with a handful of key sections, these patterns provide versatile solutions for guiding users through your application.