After having worked with Flutter for a bit, I decided to create a package to help with something that I have used quite frequently: the BLoC pattern.
For those not familiar with the BLoC pattern, it is a design pattern which helps separate the presentation layer from the business logic. You learn more about it here.
While using the BLoC pattern can prove to be challenging due to the setup as well as an understanding of Streams and Reactive Programming, at it’s core a BLoC is pretty simple:
A BLoC takes a stream of events as input and transforms them into a stream of states as output.
We can now use this powerful design pattern with the help of the bloc package.
This package abstracts reactive aspects of the pattern allowing developers to focus on converting events into states.
Let’s start by defining those terms…
Events are the input to a Bloc. They are commonly UI events such as button presses.
dispatched and converted to
States are the output of a Bloc. Presentation components can listen to the stream of states and redraw portions of themselves based on the given state (see
BlocBuilder for more details).
Transitions occur when an
mapEventToState has been called but before the bloc’s state has been updated. A
Transition consists of the
event which was dispatched, and the
Now that we understand events and states we can take a look at the Bloc API.
mapEventToState is a method that must be implemented when a class extends
Bloc. The function takes the incoming event as an argument.
mapEventToState is called whenever an event is
dispatched by the presentation layer.
mapEventToState must convert that event into a new state and return the new state in the form of a
Stream which is consumed by the presentation layer.
dispatch is a method that takes an
event and triggers
dispatch may be called from the presentation layer or from within the Bloc (see examples) and notifies the Bloc of a new
initialState is the state before any events have been processed (before
mapEventToState has ever been called).
initialState is an optional getter. If unimplemented, initialState will be
transform is a method that can be overridden to transform the
mapEventToState is called. This allows for operations like
debounce() to be used.
onTransition is a method that can be overridden to handle whenever a
Transition occurs. A
Transition occurs when a new
Event is dispatched and
mapEventToState is called.
onTransition is called before a bloc’s state has been updated. It is a great place to add bloc-specific logging/analytics.
Let’s create a counter bloc!
In order to create a Bloc, all we need to do is:
- Define our events and states
- Extend Bloc
In this case our events are
CounterEvents and our states are
CounterEvents to integers.
We can notify out
CounterBloc of events by calling
dispatch like so:
In order to observe state changes (
Transitions) we can override
Now every time we dispatch a
CounterEvent our Bloc will respond with a new
integer state and we will see a transition logged to the console.
Now let’s build a UI using Flutter and hook up the presentation layer to our
CounterBloc using the flutter_bloc package.
The flutter_bloc package provides two widgets to make interacting with Blocs easy:
BlocBuilder is a Flutter widget which requires a
Bloc and a
BlocBuilder handles building a widget in response to new states.
BlocBuilder is very similar to
StreamBuilder but has a more simple API to reduce the amount of boilerplate code needed.
BlocProvider is a Flutter widget which provides a bloc to its children via
BlocProvider.of(context). It is used as a dependency injection (DI) widget so that a single instance of a bloc can be provided to multiple widgets within a subtree.
Now let’s build our Counter App!
App widget is a
StatefulWidget which is responsible for creating and disposing a
CounterBloc. It makes the
CounterBloc available to the
CounterPage widget using the
BlocProvider widget we mentioned above.
CounterPage widget is a
StatelessWidget which uses
BlocBuilder to rebuild the UI in response to state changes from our
At this point we have successfully separated our presentational layer from our business logic layer. Notice that the
CounterPage widget knows nothing about what happens when a user taps the buttons. The widget simply tells the
CounterBloc that the user has pressed either the increment or decrement button.
That’s all there is to it!
For more examples and detailed documentation check out the official bloc documentation.
If you like the bloc library, you can support me by ⭐️the repository, or 👏 for this story.