Let’s discuss BLOC state management for Flutter

Shirsh Shukla
8 min readMar 5, 2022

This article discusses state management with the Flutter_bloc library.

Hello folks! State management is an important thing for every developer. We should be aware of these things when starting a project.

So in this article, we will discuss one of the majorly used State management Solutions, that gives us to easily manage architecture patterns also as logical management, that is flutter_bloc.

These block patterns are a widely used predictable state management library for the Flutter Application. They allow you to separate presentation logic from business logic. In this article, we will explore these patterns in greater detail.

Let’s begin with an explanation of the logic behind this state management approach, and then we’ll proceed with a coding example.

let’s get started, first of all using the block pattern you have three different layers, first you have the Presentation layer and this is simply the user interface or whatever the end-user of your app interacts with its role is very straightforward the UI in fact shows specific widgets based on the state of the block it also handles the user inputs that are translated to events and added to the block to trigger state changes.

then you have the business logic layer think of it as the brain of your block it takes the input from the presentation layer based on the end-user action and it takes as well inputs from any application life cycle events based on the input it builds new states to be consumed by the UI you can also think of this as the bridge between your data sources and your widgets in fact to build some specific application states you might need to fetch data from a data source if that’s the case the business logic layer sends a request to the data layer and finally it uses the data to build the new state.

then you have the data layer so this can be split into data providers that perform crude operations on a database. and repositories that have a different purpose which is to collect and transform the input coming from multiple data providers.

Now you might still have some doubt about what each component of the block pattern does.

however, you can try to think about this from a different perspective imagine a conversation between three-player the UI the block, and the data provider.

the conversation will start from the UI, and for example the UI will ask the block something along the lines of someone just clicking on the submit button what should I do, and at that point, the block will be like hey I need to think of it and I will let you know and at the point, the block will start another conversation with the data provider asking whether you can get some data and the provider will be like of course these are the data I’m fetching it from the database where you are and finally the conversation ends with the block going back to the UI and telling here’s your data now you need to show these widgets go on and rebuild the UI.

now if we need to think about the block pattern in a very practical way, we need to look at the Flutter block library for which this library is maintained by the same publisher of the block pattern and it makes it very easy to implement the business logic component design pattern inside your flutter apps, in fact, it provides a lot of widgets that help you to implement the block pattern and reduce the boilerplate code.

This article will look at a few of these widgets including the block provider, the block builder, the BlocSelector, the block listener, the BlocObserver, and the block consumer as well by the end of the article you will also be able to create multiple blocks that can communicate with each other so that you can use a state change of a specific block to add an event in another block and trigger a state change in the second block.

So let’s also discuss those Bloc Widgets also with this flow, because before starting implementation we know about it.

  • BlocProvider/MultiBlocProvider
    Using this widget as a dependency injection that can be applied to multiple widgets inside an application branch so that they all have access to the same instance of a bloc.
  • BlocBuilder
    The widget is constructed using BlocBuilder in response to state changes. BlocBuilder has a simpler API than StreamBuilder, which reduces boilerplate code.
    The builder function will be called many times and should return a widget in response to the state.
  • BlocListener
    A BlocListener is a Flutter widget that takes a BlocWidgetListener and an optional bloc and invokes it whenever a bloc state changes.
    The purpose of this function is to perform functionality that needs to occur once per state change, such as navigation, displaying a snack bar, displaying a dialog, etc.
  • BlocConsumer
    This widget is super useful when you need to control the states of your bloc to rebuild the widget and also for navigating or showing a dialog, etc. This widget has the listener and the builder function so you can use them together.
  • BlocSelector
    BlocSelector is similar to BlocBuilder but allows developers to filter out updates from the selected value based on the current state of the block. This prevents unnecessary builds if the selected value does not change.
  • BlocObserver
    This class is used to test or detect application states because it knows how to handle state management and error detection for every Bloc that is instantiated.

I know you are a little bit confused after reading all those things, so let’s clear all these points using a simple example.

So let’s start I am creating a simple counter application were two buttons in there. and these two buttons add and subtract integer values.

here is my main.dart file.

and here is my bloc(controller) CounterCubit.dart class.

So above you see, I have created an example of a number counter page, where CounterCubit.dart class-work as controller class.
on which I have created two functions. increment and decrement. and these operations managers by emit function.

emit allow for the possibility of notifying listeners of the initial state, emitting a state which is equal to the initial state is allowed as long as it is the first thing emitted by the instance.

Another thing is we use BlocBuilder.

BlocBuilder handles building a widget in response to new `states`. like in CounterPage class need to know which new state emit by CounterCubit class.

Now let’s see how to manage large applications where we have multiple screens. for this, we use MultiBlocProvider because,

MultiBlocProvider converts the BlocProvider list into a tree of nested BlocProvider widgets.
As a result, the only advantage of using MultiBlocProvider has improved
readability due to the reduction in nesting and boilerplate.

just see why we use this, by seeing this example

BlocProvider<CounterCubit>(
create: (BuildContext context) => CounterCubit(),
child: BlocProvider<DoubleCounterCubit>(
create: (BuildContext context) => DoubleCounterCubit(),
child:TabController(),
)
)

but this looks quite bulk and complex for multiple controller classes, so for the same thing, we made it by MultiBlocProvider.
like this,

MultiBlocProvider(
providers: [
BlocProvider<CounterCubit>(
create: (BuildContext context) => CounterCubit(),
),
BlocProvider<DoubleCounterCubit>(
create: (BuildContext context) => DoubleCounterCubit(),
)
],
child: TabController(),
)

by this way, we easy to manage.

let’s see full-example, it's my main class

and for controller class, CounterCubit class we already created above also, another class we created DoubleCounterCubit class for understanding MultiBlocProvider.

so by DoubleCounterCubit class, we controlled DoubleCounterPage double value add and subtract in this class.

Now let’s see how BlocListener help us,
the block listener differ from the block builder because we are now building different UI based on the state of the block instead here every time there is a state change we can invoke a listener by invoking the listener we can define different functions for our app for example,

for the above example as you see, as using BlocListener I am printing two different print statements as check by integer variable is even or odd. as similar as you can perform different actions like showing dialog, Toast, etc.

Now let’s see another Bloc Widget is BlocConsumer.
As the above example, we use both BlocListener and BlocBuilder because we needed both of them, BlocListener catch my action, and BlocBuilder update my UI, so what about if we needed both,
So, we have BlocConsumer Widget by using this we can perform both of BlocListener and BlocBuilder actions.

basically, BlocConsumer should only be used when it is necessary to both rebuild UI and execute other reactions to state changes in the bloc.

let’s see this example as well,

Now let’s see another Widget that is BlocSelector, this widget call according to some sort of condition,

BlocSelector functions similarly to BlocBuilder but they allow developers to filter updates based on the state of the bloc. If the selected value does not change, unnecessary builds are prevented.
With this widget, developers can filter updates based on the state of the current bloc.

let’s see this example

like as above example we show the text widget when int value is an event and when this is odd we show icons.

let’s see the last Bloc Widget is BlocObserver, that is last but not least,

BlocObserver Called whenever a Bloc is instantiated. In many cases, a cubit may be lazily instantiated.
mainly this is used for testing or detecting application state because this class knows all state management and error detection.

let’s see this example

inside there is some sort of pre-defined method, by using this we perform different actions. like

onCreate can be used to observe exactly when the cubit instance is created. this is called whenever an event is `added` to any bloc with the given bloc and event.

onEvent Called whenever a Change occurs in any bloc, a change occurs when a new state is emitted.

onChange Called whenever a transition occurs in any bloc with the given bloc and transition, also this is called before a bloc’s state has been updated.

onTransition is called before a bloc’s state has been updated.

onError Called whenever a Bloc is closed.

onClose is called just before the Bloc is closed.

So, that’s all the things, that I want to describe regarding this state management solution. yes, I am using the counter app for that because it's easy to understand for every flutter developer. if you want some more information regarding this library, please comment below.

If you got something wrong? Mention it in the comments. I would love to improve. your support means a lot to me! If you enjoy the content, I’d be grateful if you could consider subscribing to my YouTube channel as well.

I am Shirsh Shukla, a creative Developer, and a Technology lover. You can find me on LinkedIn or maybe follow me on Twitter or just walk over my portfolio for more details. And of course, you can follow me on GitHub as well.

Have a nice day!🙂

--

--

Shirsh Shukla

SDE at Reliance Jio | Mobile Application Developer | Speaker | Technical Writer | community member at Stack Overflow | Organizer @FlutterIndore