Flutter Building a Bottom Navigation Bar with "Flutter_Bloc"

| By Maina Wycliffe | | | Flutter

I was in the middle implementing a BottomNavigationBar, when a thought crossed mind. Can I use bloc pattern to manage its state? Admittedly, it’s not the most brilliant idea, not even close to one, but I thought it was a very good way to improve my understanding of the bloc pattern. So, when I was done, I decided to create my first ever post on flutter.

Introduction

This what we are building:

Blocs BottomNavigationBar Demo
Blocs BottomNavigationBar Demo

To achieve this, we are going to be using the flutter_bloc package, which depends on core bloc package. The goal of the bloc package is to simplify the implementation of the bloc pattern, by reducing the amount of boilerplate required. It has a package wrapper for both flutter and angular dart, allowing you to share your business logic between your flutter app and web app (AngularDart).

If you are unfamiliar with bloc concepts, please learn more here before you can continue, this will make it easier for you to follow this post.

Let’s get started:

Dependencies

For this project, we are going to require at least the following dependencies:

  1. Bloc – Link

  2. flutter_bloc – Link

Feel free to add any other dependencies to fit your projects needs. Assuming you have successfully installed the above dependencies, let’s start coding:

To implement a bloc for a BottomNavigationBar, we are going to need 3 things: Events, State and a Bloc.

Let’s start by implementing events:

Events

We are going to start by defining our events. In our case, we are going to have just 3 BottomNavigationBarItems, each display a different color when clicked. As such we are going to define 3 events, each representing each of BottomNavigationBarItems. This seems perfect for an enum with our three constants, for each BottomNavigationBarItem:

enum NavbarItems { Red, Green, Blue }

State

Now that we have our bloc events constants, we need to create a state for each of the BottomNavigationBarItems. But first, we let’s create a state for BottomNavigationBar, then the other 3 states will extend it – the parent state.

abstract class NavbarState {}

And then, for each of our items states, we shall hold the Scaffold Widgets title and its BottomNavigationBarItems index. This will allows us to highlight the active item and set the page title.

For instance, for our ShowRed state, it will look like this:

class ShowRed extends NavbarState {
  final String title = "Red";
  final int itemIndex = 0;
}

And the same applies to our other two states:

// ShowBlue State
class ShowBlue extends NavbarState {
  final String title = "Blue";
  final int itemIndex = 1;
}


// ShowGreen State
class ShowGreen extends NavbarState {
  final String title = "Green";
  final int itemIndex = 2;
}

And that’s it for our state, next let’s build our bloc.

Bloc

Our bloc will simply map events to states. For instance, our Red event will be mapped to the ShowRed state and so on. Let’s define our bloc, which will extend the bloc class, which takes in events and maps it to a state. So, for our case, we will be mapping NavbarItems events we defined above to the NavbarState, our parent state.

class NavbarBloc extends Bloc<NavbarItems, NavbarState> {
  // object code here ...
}

Then, inside our bloc, let’s add an initial state – which will be the default selected item. This is done by using the initialState method. The initialState method is triggered before any event has been dispatched. For our case, the initial state will be ShowRed state;

@override
NavbarState get initialState => ShowRed();

Next, we must define a mapEventToState method inside our bloc. This is triggered each time an event is dispatched, for instance, clicking on a BottomNavigationBarItem. The method accepts the current state and the event that triggered it and streams the current state. Then inside the presentation layer, you can listed to this stream and react accordingly.

In our case, we only have 3 events – Red, Blue and Green. So, we will map them to the appropriate state. For instance, when the event that was dispatched was Red, then will map that to the ShowRed state and so on. To achieve this, we will use a simple switch case:

@override

Stream<NavbarState> mapEventToState(NavbarState state, NavbarItems event) async* {
  switch (event) {
    case NavbarItems.Blue:
      yield ShowBlue();
      break;
    case NavbarItems.Green:
      yield ShowGreen();
      break;
    default:
      yield ShowRed();
      break;
  }
}

And that’s it for our bloc. Now let’s move to the presentation layer:

Presentation

Next, let’s build our User Interface. We are going to wrap our scaffold widget with a BlocBuilder, so we can be able to change the title, body and set selected item index.

First, let’s define a local variable for our bloc:

NavbarBloc _navbarBloc;

Next, on state initialization, lets set our bloc variable from above to the BottomNavigationBar Bloc:

@override
void initState() {
  super.initState();
  _navbarBloc = NavbarBloc();
}

And then, we need to be able to dispose of our bloc, when the stateful widget is disposed:

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

Then, we are going to use BlocBuilder to provide our BottomNavigationBar bloc above to build our UI based on the selected state. But first, to make things a tad bit easier, lets create a method to build our Scaffold widget. Let’s call it buildHomepage, and will accept title, color and currentIndex. And it is going to return a Scaffold widget.

Scaffold buildHomepage(String title, Color color, int currentIndex) {
  return Scaffold(
   appBar: AppBar(
     title: Text(title),
   ),
   body: Container(
     color: color,
     child: Center(
       child: Text(title),
     ),
   ),
   bottomNavigationBar: BottomNavigationBar(
     currentIndex: currentIndex,
     onTap: (index) {
       // on tap method here
     },
     items: [
       BottomNavigationBarItem(
         icon: Icon(Icons.looks_one),
         title: Text("Red"),
       ),
       BottomNavigationBarItem(
          icon: Icon(Icons.looks_two),
          title: Text("Blue"),
       ),
       BottomNavigationBarItem(
         icon: Icon(Icons.looks_3),
         title: Text("Green"),
       )
    ],
  ),
);

Now, we can use the BlocBuilder to provide our bloc in order to build the UI based on the selected BottomNavigationBar state:

@override

Widget build(BuildContext context) {
  return BlocBuilder(
    bloc: _navbarBloc,
    builder: (BuildContext context, NavbarState state) {
      // check state and show appropriate content here
    },
  );
}

Then, inside the builder method, we will check which is the current state and show the appropriate content. For instance, when the state is ShowRed:

if (state is ShowRed)
  return buildHomepage(state.title, Colors.red, state.itemIndex);

And the same applies to the other two states:

if (state is ShowBlue)
  return buildHomepage(state.title, Colors.blue, state.itemIndex);

if (state is ShowGreen)
  return buildHomepage(state.title, Colors.green, state.itemIndex);

Now, the only thing that’s remaining is to dispatch the actions whenever a BottomNavigationBarItem is tapped, using the onTap event listener:

onTap: (index) {
  if (index == 0) _navbarBloc.dispatch(NavbarItems.Red);
  if (index == 1) _navbarBloc.dispatch(NavbarItems.Blue);
  if (index == 2) _navbarBloc.dispatch(NavbarItems.Green);
}

And that’s it, you now have a BottomNavigationBar that uses bloc pattern to keep track of the selected items. You can find the whole source code here.``

Comments