Implementing BLoC pattern using flutter_bloc
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:
BLoC Architecture
A BLoC takes a stream of events as input and transforms them into a stream of states as output.
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 builder
function. 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 Repository
so 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 willdispatch
aCountrySelectedEvent
event with the given countryId and our bloc will responsible for figuring out what players are there in the team and returning a newState
.SearchTextChangedEvent
: Whenever a user types the name of a player in the search bar, we willdispatch
aSearchTextChangedEvent
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 newState
.
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 dispatchesCountrySelectedEvent
.Search Bar
- contains a TextField and changing its content dispatchesSearchTextChangedEvent.
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.