Implementing BLoC pattern using flutter_bloc

Piyush Sinha
Flutter Community
Published in
6 min readSep 27, 2019

--

In this blog we will learn how to implement BLoC pattern using flutter_bloc package. It is a design pattern which helps separate the presentation layer from the business logic.

Thanks Felix Angelov for this awesome package & Techie Blossom for an amazing tutorial.

To understand flutter_bloc we will create a demo of hitting an API that brings in the football players details. We will create multiple states for an event and see how bloc provider and bloc builder are used to manage state of the app.

The finished product will look like this:

Ignore the UI 😅. This app is all about understanding flutter_bloc.

BLoC Architecture

A BLoC takes a stream of events as input and transforms them into a stream of states as output.

Events???States???

Events are actions that occurs as a result of the user interaction with the app such as button pressed. Events are dispatched and converted to States with the help of a function named mapEventToState .State is the information that can be read synchronously when the widget is built and might change during the lifetime of the widget.

Quick overview of the package

In flutter_bloc package we have :

BlocProvider

BlocProvider is a Flutter widget(Inherited widget) which provides a bloc to its child via BlocProvider.of(context). Child widget gets updated whenever there is any change in the bloc(Business Logic Components).

BlocProvider(
builder: (BuildContext context) => BlocA(),
child: ChildA(),
);

BlocBuilder

BlocBuilder is a Flutter widget which requires a Bloc and a builderfunction. BlocBuilder handles building a widget in response to new states.

Let’s Start

Setup

The first step is to add the required plugins as a dependency in the pubspec.yaml file.

dependencies:
http: ^0.12.0+2
flutter_bloc: ^0.21.0

Next, run flutter packages get to install all the dependencies.

API

For this application we will be hitting the EA Sports API.

Our base Url is https://www.easports.com/fifa/ultimate-team/api/fut/item?"

We’ll be focusing on two endpoints:

  • /api/fut/item?country=$countryId to get list of players for a given country.
  • /api/fut/item?name=$name to get list of players for a given name belonging to any country.

Data Model

Open https://www.easports.com/fifa/ultimate-team/api/fut/item?country=52 in your browser and you’ll see the following response:

Copy this response and use https://javiercbk.github.io/json_to_dart/ to get the dart code. Create a models directory inside lib directory and create a file inside that api_models.dart and paste the copied code there.

Data Provider

The player_api_provider is the lowest layer in our application architecture (the data provider). Its only responsibility is to fetch data directly from our API.

As we mentioned earlier, we are going to be hitting two endpoints so our player_api_provider needs to expose two public methods:

  • fetchPlayersByCountry(String countryId)
  • fetchPlayersByName(String name)

It should look something like this:

Hey, this is a truncated version, get full version https://gist.github.com/piyushsinha24/03c94ecfd7df3997805e5caeadfd20f2

Repository

The repository.dart serves as an abstraction between the client code and the data provider so that as a developer working on features, you don't have to know where the data is coming from. It may come from API provider or Local database. So a good practice is to use Repository pattern.

This is how it is done:

Awesome! We are now ready to move up to the business logic layer.

Business Logic (Bloc)

Our PlayerListingBloc is responsible for receiving PlayerListingEvents and converting them into PlayerListingStates. It will have a dependency on Repositoryso that it can retrieve the Players when a user taps on the country flag of their choice or types the name of the player in the search bar.

Before jumping into the Bloc we need to define what Events our PlayerListingBloc will be handling as well as how we are going to represent our States.

PlayerListingEvent

We have two events in this application:

  • CountrySelectedEvent : Whenever a user taps a country flag, we will dispatch a CountrySelectedEvent event with the given countryId and our bloc will responsible for figuring out what players are there in the team and returning a new State.
  • SearchTextChangedEvent : Whenever a user types the name of a player in the search bar, we will dispatch a SearchTextChangedEvent event with the given name and our bloc will responsible for figuring out what players are there in any of the country in the API with the matching name and returning a new State.

It should look something like this:

PlayerListingStates

For the current application, we will have five possible states:

  • UninitialisedState - our initial state which will have no player data because the user has not fired any event.
  • FetchingState - a state which will occur while we are fetching the player data.
  • FetchedState - a state which will occur if we were able to successfully fetch player data.
  • ErrorState - a state which will occur if we were unable to fetch player data.
  • EmptyState - a state which will occur if we were able to fetch player data but found no match for the user request.

It should look something like this:

Now that we have our Events and our States defined and implemented we are ready to make our PlayerListingBloc.

PlayerListingBloc

Our PlayerListingBloc is very straightforward. To recap, it converts PlayerListingEvents into PlayerListingStates and has a dependency on the Repository.

We set our initialState to UninitialisedState since initially, the user has not fired any event. Then, all that's left is to implement mapEventToState.

If our event is CountrySelectedEvent then we will yield our FetchingState state and then try to get the player data from the Repository using fetchPlayersByCountry(String countryId) .

If our event is SearchTextChangedEvent then we will yield our FetchingState state and then try to get the player data from the Repository using fetchPlayersByName(String name) .

After successful fetching, if player data is null then we yield our EmptyState state else we yield our FetchedState state.

If the fetching failed due to some exception then we yield ourErrorState state.

See the code to understand the process:

Hey, this is a truncated version, get full version https://gist.github.com/piyushsinha24/912a9bbc9e0aae8c678d41dac3db6f98

That’s all there is to it! Now we’re ready to move on to the final layer: the presentation layer.

Presentation

Our App widget is going to start off as a StatelessWidget which has the Repository injected and builds the MaterialApp with our widgets.

Our Home widget will be a StatefulWidget responsible for creating and disposing a PlayerListingBloc.

All that’s happening in this widget is we’re using BlocBuilder with our PlayerListingBloc in order to rebuild our UI based on state changes in our PlayerListingBloc.

Inside the scaffold, we have a column with three children widgets:

  • Horizontal Bar- contains a ListView.builder with horizontal scroll direction and has nation flags as items tapping on which dispatches CountrySelectedEvent .
  • Search Bar- contains a TextField and changing its content dispatches SearchTextChangedEvent.
  • Player Listing- checks for the current state of the application and returns a widget accordingly. For fetched state, it returns a ListView having ListTiles as children displaying player data and all wrapped with the Expanded widget. For Fetching state, it returns a CircularProgressIndicator. For other states, it returns a message.

From Home widget, tapping on any of the ListTiles will take us to PlayerProfile which is another Stateful widget which displays details specific to a player.

This blog wasn't about UI/UX so I won’t be discussing about that in depth.

🎉 That’s all there is to it! We’ve now successfully implemented our app in flutter using the bloc and flutter_bloc packages and we’ve successfully separated our presentation layer from our business logic. I hope you enjoyed it, and leave a few claps 👏 if you did. 🎉

The full source for this example can be found here. If you find it useful please support me by ⭐️ the repository .

Follow me for more Flutter articles and comment for any feedback you might have about this article.

--

--