How to Create a BottomNavigationBar with Flutter
Tags: flutter
Published: 2020-04-27 15:31:35

---
title: How to Create a BottomNavigationBar with Flutter
published: true
date: 2020-04-26 14:30:17 UTC
tags: flutter
canonical_url: 
---

 ![How to Create a BottomNavigationBar with Flutter](https://developer.school/content/images/2020/04/tab.jpg)

In this article we'll be creating a `BottomNavigationBar` with the ability to switch between different tabs using [IndexedStack](https://api.flutter.dev/flutter/widgets/IndexedStack-class.html). You'll want to use this when creating tab based user interface(s) with their own navigation stack.

<iframe width="480" height="270" src="https://www.youtube.com/embed/Qe_OYfEasnQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Our example application will be simple. It'll consist of four screens total, three of which are "main" tab pages, the final one being a detail page that we push on the navigation stack.

#### New Project

Let's create a new Flutter project in the terminal:

```
# New Flutter project
$ flutter create flutter_shopping

# Open in editor
$ cd flutter_tabs && code .
```

#### Page Creation

We can start off by creating our _main_ pages that will be displayed in our tab system. These will be simple `StatelessWidget`s and the content doesn't matter for our tutorial.

```
/// lib/presentation/shop/pages/shop_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_shopping/presentation/product_detail/pages/product_detail_page.dart';

class ShopPage extends StatelessWidget {
  static Route<dynamic> route() => MaterialPageRoute(
        builder: (context) => ShopPage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Shop"),
      ),
      body: Center(
        child: FlatButton(
          onPressed: () => Navigator.of(context).push(
            ProductDetailPage.route(),
          ),
          child: Text("Navigate to Product Detail Page"),
        ),
      ),
    );
  }
}


/// lib/presentation/home/pages/home_page.dart
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  static Route<dynamic> route() => MaterialPageRoute(
        builder: (context) => HomePage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home"),
      ),
      body: Center(
        child: Text("Hello, Home!"),
      ),
    );
  }
}


/// lib/presentation/search/pages/search_page.dart
import 'package:flutter/material.dart';

class SearchPage extends StatelessWidget {
  static Route<dynamic> route() => MaterialPageRoute(
        builder: (context) => SearchPage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Search"),
      ),
      body: Center(
        child: Text("Hello, Search!"),
      ),
    );
  }
}


/// lib/presentation/product_detail/pages/product_detail_page.dart
import 'package:flutter/material.dart';

class ProductDetailPage extends StatelessWidget {
  static Route<dynamic> route() => MaterialPageRoute(
        builder: (context) => ProductDetailPage(),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Product Detail"),
      ),
      body: Center(
        child: Text("Hello, Product!"),
      ),
    );
  }
}

```

#### TabPage and BottomNavigationBar

Now that we've got the ability to show various pages (i.e. `HomePage`, `SearchPage`, and so on), we'll create a `TabPage`. This page will use `IndexedStack` to display a different body _depending_ on the current tab selected by the user.

In order to make this easier to generate, let's create a `TabNavigationItem` to hold information about our tab:

```
/// lib/presentation/tabs/models/tab_navigation_item.dart
import 'package:flutter/widgets.dart';

class TabNavigationItem {
  final Widget page;
  final Widget title;
  final Icon icon;

  TabNavigationItem({
    @required this.page,
    @required this.title,
    @required this.icon,
  });
  
  static List<TabNavigationItem> get items => [
        TabNavigationItem(
          page: HomePage(),
          icon: Icon(Icons.home),
          title: Text("Home"),
        ),
        TabNavigationItem(
          page: ShopPage(),
          icon: Icon(Icons.shopping_basket),
          title: Text("Shop"),
        ),
        TabNavigationItem(
          page: SearchPage(),
          icon: Icon(Icons.search),
          title: Text("Search"),
        ),
      ];
}

```

We can then use this _and our other pages_ to create a `TabsPage` which will use our aforementioned `IndexedStack` with a `_currentIndex` that changes whenever a user `taps` a tab.

```
import 'package:flutter/material.dart';
import 'package:flutter_shopping/presentation/home/pages/home_page.dart';
import 'package:flutter_shopping/presentation/search/pages/search_page.dart';
import 'package:flutter_shopping/presentation/shop/pages/shop_page.dart';
import 'package:flutter_shopping/presentation/tabs/models/tab_navigation_item.dart';

class TabsPage extends StatefulWidget {
  @override
  _TabsPageState createState() => _TabsPageState();
}

class _TabsPageState extends State<TabsPage> {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: [
          for (final tabItem in TabNavigationItem.items) tabItem.page,
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (int index) => setState(() => _currentIndex = index),
        items: [
          for (final tabItem in TabNavigationItem.items)
            BottomNavigationBarItem(
              icon: tabItem.icon,
              title: tabItem.title,
            )
        ],
      ),
    );
  }
}

```

#### How does IndexedStack work?

Given a list of Widgets (i.e. the `children` of `HomePage`, `ShopPage` and `SearchPage`), it'll display the Widget where the `children` matches the `currentIndex`.

For example, if our `currentIndex` was `1` and our `children` array looked like:

```
children: [
  HomePage()
  ShopPage()
  SearchPage()
]
```

Our `IndexedStack` would display the `ShopPage`.

We'll now be able to update our `main.dart` to place `TabsPage` as our `home` entry:

```
import 'package:flutter/material.dart';
import 'package:flutter_shopping/presentation/tabs/pages/tabs_page.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'App',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: TabsPage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

```

This gives us the following result:

<iframe width="480" height="270" src="https://www.youtube.com/embed/CSibwFiNQdU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

#### Summary

We've now used the `IndexedStack` to switch our displayed widget depending on the item selected by the user!

Code for this article: [https://github.com/PaulHalliday/flutter\_indexed\_stack\_tab\_view](https://github.com/PaulHalliday/flutter_indexed_stack_tab_view)