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:
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:
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.``