AUG 16, 2019

At OakTree Apps, we are big fans of Flutter. We truly believe that Flutter has tremendous potential to disrupt the mobile market. Our reasons are multiple, and maybe we will leave that for a different post in the future.

Today, however, we shall see how to add a TabBar and a BottomNavigation to your Flutter app.

So, let’s start with what is a TabBar View and a BottomNavigationBar? The official Flutter documentation states the following:

TabBar

A material design widget that displays a horizontal row of tabs. Typically created as the AppBar.bottom part of an AppBar and in conjunction with a TabBarView. If a TabController is not provided, then a DefaultTabController ancestor must be provided instead. The tab controller’s TabController.length must equal the length of the tabs list and the length of the TabBarView.children list.

BottomNavigationBar

A material widget that’s displayed at the bottom of an app for selecting among a small number of views, typically between three and five. The bottom navigation bar consists of multiple items in the form of text labels, icons, or both, laid out on top of a piece of material. It provides quick navigation between the top-level views of an app. For larger screens, side navigation may be a better fit. A bottom navigation bar is usually used in conjunction with a Scaffold, where it is provided as the Scaffold.bottomNavigationBar argument.

Seems fairly straightforward, doesn’t it?

We initially struggled with trying to determine what is the best approach to incorporate both before eventually settling into one. So enough talking and let’s start coding.

 

 

Go ahead and create a new Flutter project. After the project has been created successfully, your main.dart would look something similar to this:

 

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      //new
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

 

The MyHomePage class points to a home page that will show a TabBarView and BottomNavigationBar working together. Here is what the final MyHomePage class looks like:

 

class MyHomePage extends StatefulWidget {

  @override
  State createState() {
    return _MyHomePage();
  }

}

class _MyHomePage extends State{
  int _selectedIndexForBottomNavigationBar = 0;
  int _selectedIndexForTabBar = 0;


  //1
  static List _listOfIconsForBottomNavigationBar = [
    Icon(Icons.directions_car),
    Icon(Icons.directions_walk),
    Icon(Icons.directions_bike),
  ];


  //2
  static List _listOfIconsForTabBar = [
    Icon(Icons.directions_boat),
    Icon(Icons.directions_bus),
    Icon(Icons.directions_railway),
  ];


  //3
  void _onItemTappedForBottomNavigationBar(int index) {
    setState(() {
      _selectedIndexForBottomNavigationBar = index;
      _selectedIndexForTabBar = 0;
    });
  }


  //4
  void _onItemTappedForTabBar(int index) {
    setState(() {
      _selectedIndexForTabBar = index+1;
      _selectedIndexForBottomNavigationBar = 0;
    });
  }

  @override
  Widget build(BuildContext context) {


    //5
    final tabBar = new TabBar(labelColor: Colors.blue,
      onTap: _onItemTappedForTabBar,
      tabs: [
        new Tab(
          text: "BOAT",
        ),
        new Tab(
          text: "BUS",
        ),
        new Tab(
          text: "RAILWAY",
        ),
      ],
    );



    //6
    return new DefaultTabController(length: 3, child: new Scaffold(
      appBar: AppBar(bottom: tabBar,backgroundColor: Colors.white, title: Text('Tabs Demo')),

      //7
      body: Center(child:_selectedIndexForTabBar == 0 ?
      _listOfIconsForBottomNavigationBar.elementAt(_selectedIndexForBottomNavigationBar):
      _listOfIconsForTabBar.elementAt(_selectedIndexForTabBar - 1)),

      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        onTap: _onItemTappedForBottomNavigationBar, // this will be set when a new tab is tapped
        items: [
          BottomNavigationBarItem(
              icon: new Icon(Icons.directions_car),title: Text('CAR')),
          BottomNavigationBarItem(
              icon: new Icon(Icons.directions_walk),title: Text('WALK')),
          BottomNavigationBarItem(
              icon: Icon(Icons.directions_bike),title: Text('BIKE')),

        ],
        currentIndex: _selectedIndexForBottomNavigationBar,
      ),
    ));
  }

}

 

You are probably confused at this point as to what is happening in the file.

 

 

Let’s unpack the file one step at a time:

  1. We create a static list of icons that will be displayed when the user selects any one of the tabs from BottomNavigationBar.
  2. We create another static list of icons that will be displayed when the user selects any one of the tabs from TabBar. We could have reused the same list but for clarity purposes, we have two separate lists.
  3. _onItemTappedForBottomNavigationBar is called when the user selects any tab from the BottomNavigationBar. We pass the index of the selected tab as a parameter so that we can fetch the appropriate image from the list based on the index. In this function, we reset the _selectedIndexForTabBar variable to 0 to avoid overlap between two images.
  4. _onItemTappedForTabBar is called when the user selects any tab from the TabBar. We again pass the index of the selected tab as a parameter so that we can fetch the appropriate image from the list based on the index. Similar to the previous point, we reset the _selectedIndexForBottomNavigationBar variable to 0 to avoid overlap between two images.

You may also notice that _selectedIndexForTabBar is incremented by 1. Why is that?
That is done to help the build function determine which image to display.

  1. We are creating the TabBar with the textColor being blue, and we are passing a list of widgets for the tabs. As you can also see, we have an onTap parameter which is assigned to _onItemTappedForTabBar.
  2. We create a DefaultTabController to host out TabBar. Flutter requires that while creating a TabBar we either use the DefaultTabController or provide our own TabController. If neither is provided there will be a runtime exception. We are passing the length for the DefaultTabController as 3 to display the appropriate number of tabs.
  3. We are using both the variables to determine what image should be displayed when the user selects a tab from either the TabBar or BottomNavigationBar.
    If _selectedIndexForTabBar == 0, we tell the build function to fetch an image from the _listOfIconsForBottomNavigationBar list and vice versa.

The final app looks something like this:

 

In this tutorial, we learned how to get a bottom navigation bar working together with a Tab bar.

We are a mobile-first development agency. Check our website or send us an email at pk@oaktreeapps.com with your thoughts or questions.