Beginner Flutter to BLoC State Management with Cubits

(As of July 2020, Cubit has been merged into the official Bloc library).

Like me, maybe you’ve been using Flutter for a while. Whether you’re a beginner or not Flutter uses its own Widget-based implementation which takes getting used to.

Remember, Flutter uses a hierarchy (or tree) of Widgets. That means it is far better to handle state higher up on the tree. It is possible to access state from lower Widgets using callbacks, but an app of any complexity will quickly induce coder fatigue when forced to pass around many callbacks.

Like Blocs except simpler

This is where Cubit comes in. Cubit is a state management library designed to simplify the BLoC design pattern. It is designed for programmers who do not have lots of experience in reactive programming, so if you’re a beginner it’s a good idea to start here when building more complicated apps.

Quick primer: BLoC stands for Business Logic Components. It is Flutter’s version of MVC, however while it is disingenuous to label it so, that debate is outside the scope of this article. Providers are included as part of the BLoC pattern.

To understand the concept of a reactive app, Flutter is designed to be “Declarative” which means our job is to declare the state of the application, and Flutter will build the resulting UI. It is crucial to understand that Flutter does not actually modify the UI, instead it rebuilds the UI in real time whenever a state change is detected during runtime (ie. due to user interaction). The entire UI does not have to rebuild itself, only the Widgets that have had their state updated.

Widget Tree (Official Flutter Docs)

You might naively say “but Andrew, what if I don’t care about state management?” To that I say, whenever you want to make an external API call you will have to use Flutter’s in-built http request method. This method is of type Future<Response> and to pass around that response throughout your app, you will have to rely on asynchronous code. This will be fine for smaller projects, but larger projects will end up with Futures being passed around all over the place, and at some point it will just take less time to learn a proper state management technique.

The best way to learn is to start. The bloclibrary.dev site has a wonderful series of tutorials, we are going to take two of them and help guide you through them without confusing you.

First we will create a Counter App.

This app will simply have a + button and a — minus button. The UI will show the current number starting from 0, counting up and down. Let’s do this.

Step 1. Quick Start

Run “flutter create app_name”

Edit your pubspec.yaml so the dependencies look like this:

Save then run ‘flutter packages get’ (Visual Studio Code does this automatically).

All files and folders outside ‘lib’ exist for flutter’s cross-platform compilation. All we need to do is work inside lib. Create files and folders inside ‘lib’ as follows, we’ll discuss later:

main.dart
app.dart

Let’s create folders here (rule of thumb: it’s always better to create more folders than less)

counter
counter/view
counter/cubit

Okay now we create these files:

counter/counter.dart
counter/view/counter_view.dart
counter/view/counter_page.dart
counter/cubit/counter_cubit.dart

Step 2. main.dart and app.dart

These are entry points into our app. When the app starts, it will launch main.dart which will lead into app.dart which sets counter_page.dart as the home page. If you’re new that might seem silly, but get used to it, it’s always better for things to be spread out than packed into one tiny file.

We’ll replace the contents of main.dart with the following:

We have not created CounterApp() yet, we will do that now.

In app.dart write the following:

Think of MaterialApp as bootstrap for mobile, it’s a base UI kit that Flutter has great support for.

The keys are state variables that are passed around that help keep track of our state. Luckily we don’t have to manage them, the BLoC/Cubit design pattern handles them for us.

Step 3: counter_cubit.dart and counter_page.dart

CounterCubit() is the setter methods for our state.

CounterPage() creates an instance of CounterCubit(). Then serves it to the View.

We could create this in one file, but again we are following the design philosophy of separating/decoupling the different parts of our app.

An example of a benefit is that we could create another set of + and — buttons which control a separate counter (with separate states) but re-use the same code. The benefits are endless.

Anyway open counter_cubit.dart and write:

Pretty simple, yes? Good.
(Yes those are event emitters).

Next is counter_page.dart which introduces a new concept:

Not as simple, yes? Good.

Let’s go through that slowly.

CounterPage extends StatelessWidget. That’s right, BLoC pattern does not use StatefulWidgets because the BLoC library itself handles state instead, so we can forget about it.

BlocProvider is a new concept. It is based on another system of state management called providers. The BLoC pattern uses providers as part of its library. The BlocProvider creates the cubit, and the child is given access to the cubit. The “(_) =>” syntax is just a nameless function.

So in this case, CounterView() is given access to an instance of CounterCubit().

Before explaining further, let’s create our view.

Step 4: counter_view.dart

Here it all comes together.

I’m not going to explain how the UI is written.

Notice inside the main body, there’s a BlocBuilder widget? This will listen for any changes made in CounterCubit() and then rebuild itself using the new state.

In our FloatingActionButton widgets, there are onPressed properties. When the user presses these buttons, they will fire off the setter methods inside CounterCubit(), which emit events that are picked up by our BlocBuilder Widget.

In a Flutter perspective, what’s really happening is that these lower child widgets are sending state information to higher widgets on the Widget Tree. This is necessary in any complex application. We don’t want to do it with callbacks, we do it with BLoC pattern state management.

Step 5: counter.dart

We just need to wrap up and create our ‘Barrel’ which is BLoC’s way of exporting our public functions.

Open counter.dart and write this:

Type ‘flutter run’ to see the app in action.

I hope this helped. I will add the second example with API networking in the next part.

Leave a like, and comment for any questions.