Flutter Bloc: An introduction (with cubit)

Kefeh Collins
CodeX
Published in
8 min readJun 8, 2022

The Chronicles of Flutter state management 10.

Having many state management solutions in flutter is one thing I especially love about it. These different state management solutions provided, don’t just give us ideas or rules to manage the state in our apps based on the laid down “protocols” by the said package, but they also help us enforce a particular design pattern and folder structures which in some cases make a certain solution more preferable to the other on a particular project you are working on.

Bloc, in essence, is a state management solution built on top of streams and the concept of reactive programming design, using providers to facilitate its injection into the widget tree.

Here, I am assuming that you have prior knowledge of Providers(not too mandatory), build context and Notifiers such as ChangeNotifiers and StateNotifiers. And if you don’t, please feel free to check out this lovely article written by yours truly.

You may be asking why you should care about bloc with so many other state management solutions around.

I’ll actually tell you to care.

Why?

Bloc is built with a different concept of notifying for changes, rather than using notifiers, it uses streams and whoever needs to consume states, just subscribes to a particular stream for that state and whenever there is a new change to the state, it is simply emitted and is used (yeah that’s not how simple it is, but you get the idea). Flutter and dart already have built-in methods for consuming streams with error handling and different state out of the box, this is a great use case.

So if you have an app heavily reliant on streams or need ”stream-like” control, then consider using bloc.

That’s not all, is it?

Of Course not.

Actually, Bloc helps us

  1. Decouple business logic from presentation and have one source of truth for the states and a means of communication between the logic and presentation. Yeah I know, most state management solutions do that, but it’s worth noting that bloc does that too.
  2. Bloc makes state changes predictable, and easy to replay previous states and track state changes since it is a stream of state values. Sounds cool right? Implementing something like undo and redo becomes hassle-free.
  3. It facilitates code reuse, within your app and outside as Bloc code is pure dart code that can be flexible and reusable between angular dart and flutter and all dart frontend flavors.
  4. Reactive programming in dart (RxDart) is quite powerful and the fact that bloc is built on top of it means you can tap into the capabilities of RxDart if needed, but if not, bloc provides enough simple abstractions that are great to use for simple solutions. It’s worth noting that the fact that bloc is built on top of RxDart etc doesn’t make it heavy or cumbersome, RxDart is just Dart after all.

In essence, every good flutter developer wants to take account of every state of their app, loading, loaded, error state etc for every interaction of the app and hence react appropriately. Bloc is built with this in mind, to make it as easy to listen to those interactions and respond appropriately with the states that matter, not forgetting the ease with which it makes testing.

Great, now that we care about bloc 😅, let’s take a look at how to use it.

To start, bloc has two different abstractions, cubit and bloc, (that extend a class BlocBase)

Cubit is the simple case bloc, a stream component that uses functions to communicate interactions from widgets to the cubit and a stream that widgets consume for which state changes are emitted. (actually, it’s not just about widgets as the source of interaction, but the fact that cubits can expose functions used for triggering state changes etc ).

In structure, Cubit looks quite similar to StateNotifiers making it palatable to an extent for those coming from Provider (Riverpod background).

Let’s take a look at a simple example with Cubit

For a start, let us install bloc,

There are two packages of concern here, bloc and flutter bloc. Bloc is the core of the bloc package and contains the main core building blocks like Blocs and Cubits and is independent of flutter (can be used by any dart framework). Flutter_bloc is a package that contains elements that you will use in building UIs like BlocProvider and BlocBuilder, which are dependent on flutter.

You can use the add dependency functionality in vs code and search for flutter_bloc and bloc or another way we can also run flutter pub add flutter_bloc and flutter pub add bloc to add the two packages.

They will then be added to your pubspec.yaml

Now that we have that running, let’s try to implement a simple random number generator, that returns a number in the range of 1–6, let’s say a low-level dice roll.

So, we start by creating a cubit to handle this

A Cubit is just a class that extends (inherits) from BlocBase, and due to its definition, it always requires an initial state. If we don’t specify the state type, the state is assumed automatically to be of type dynamic.

As simple as that, we have created our cubit, which we want to have an integer state, but null when no dice roll has been done. We can go ahead and use it in our application.

So the idea is this, we have a blue dice (actually it’s just a blue square :-)) that when clicked shows us a random side of the dice (displays any number in the range of 1–6), initially before any clicks, we want to prompt the user to click. To begin, just like inheritedWidgets and Providers, to be able to use any cubit we have created, we need to inject it into the widget tree at a point which will be accessible to the widget we are providing for. In our case we inject it in the MyApp Widget, using the Widget from the flutter_bloc library called BlocProvider.

BlocProvider Takes a [Create] function that is responsible for creating the [Bloc] or [Cubit] and a [child] which will have access to the instance via BlocProvider.of(context). It is used as a dependency injection (DI) widget so that a single instance of a [Bloc] or [Cubit] can be provided to multiple widgets within a subtree.

So putting creating and providing our Cubit in the MyApp Widget means every widget below it will have access to the Cubit.

After this, we proceed to use our cubit. We can use it either using the widget BlocBuilder as such

Or use the extensions on the context that provide us with methods like read, watch and select as such.

use of extension methods watch

But why do they differ you may ask?

  • There is this main constraint on us flutter devs on the performance of our apps, to make sure we make use of the minimal number of rebuilds for our widgets as possible, therefore being able to constrain rebuilds to only the necessary widgets will help a lot in performance. So if you have a standalone widget that depends entirely on the state provided by the Cubit and so will always be rebuilt if the state changes, then going with context.watch is the way to go. But if you have a larger widget that only a certain portion of the widget depends on the Cubit/bloc, it’s appropriate to constrain the rebuild to just that widget using the BlocBuilder.
  • BlocBuilder is built specifically to manage the lifecycle of the bloc and as such is very appropriate to use in the widget tree, except if you want to make use of the state or Cubit/bloc in a function such as in an onTap where the extension methods will then come in handy or if the specific widget that needs scoped rebuilds, depends on more than 1 Cubit/bloc in which case you use something.
a sample of a builder that contains more than one states in its body

let’s get back on track here now, should we? 😅

So we have our Cubit used on our widget, but if we realise, we have no way of changing the value, so, how do we go about it?

In our cubit we want to be able to produce a new state, fortunately for us, there is a state variable exposed for us to use, unfortunately, we can not just go around assigning new state values to the state variable, because, this exposed state variable is actually a getter under the hood, which means, we can only read its value and not set a new one directly. This makes sense because we want that whenever we set a new state, we want it to notify all areas using the state that it has been updated should in case a rebuild is needed and then provide the new updated value, for those who have used ChangeNotifiers, you would realize that to do this, we update the state and then call a function “notifyListeners” to do that job for us. Instead, the bloc library handles this for us and provides us with a method called emit, this emit method, does exactly what it says, emit a new state to all those listening for it, and does nothing if the state emitted is the same as the previous state, this helps us, in fact, to prevent unnecessary rebuilds when there are no apparent changes.

And then we update our onTap Method to call the rollDice method when tapped as such

So we are done, we have explored the Cubit class and introduced ourselves to flutter_bloc. You can find the full working code below. In the next article, we will be diving deep into the Bloc class, how it is an upgrade from the Cubit and how to make the most of the bloc package.

https://github.com/kefeh/bloc_example/blob/main/lib/main.dart

Thanks for reading.

--

--

Kefeh Collins
CodeX
Writer for

An Enthusiastic problem solver, uses code as a tool for problem solving.