How To Write Reactive Apps in Flutter Using Flutter Bloc

Demystifying MultiBlocProviders while making a great user experience in the process.

Agustinus Theodorus
Better Programming

--

Hummingbirds
Photo by James Wainscoat on Unsplash.

Flutter has taken the world by storm. More people are migrating to it and more corporations are using it to build their apps. The reason behind the migration is not Flutter itself but rather the ecosystem maturing in the last few years.

If you have ever coded a Flutter app, you might know of a library named Cubit. Cubit is a state management library that helps implement reactive programming. When building mobile apps, you need some kind of state management. Otherwise, your data will be all over the place.

But we will not be talking about Cubit at all today. We will be discussing Flutter BLoC. More specifically, how we can implement multi-bloc providers in Flutter using the flutter_bloc library.

What Is Flutter BLoC?

flutter_bloc is a state management library meant to be a continuation of the previously mentioned Cubitlibrary. It provides an easy-to-use implementation of the BLoC pattern. You no longer need to write your own streams and event subscriptions from scratch because flutter_bloc will do it all for you.

What is the BLoC pattern?

BLoC stands for “business logic component.” It is a pattern developed by Flutter developers to solve the separation of concerns problem. In Flutter, you can mix frontend code and business logic, making it super easy to create unreadable spaghetti code. Moreover, clean Flutter code usually involves using indentation, which can make your mess both long and wide (literally!).

Developers also choose to implement the BLoC pattern because it has an inherent reactiveness. You can make your apps more reactive with BLoC by creating listeners. Each listener will trigger UI rebuilds every time it receives events. Essentially, we will subscribe our UI to data streams.

What are BlocProviders?

Bloc providers initialize the bloc and provide it to its children. It is an injection mechanism of sorts. Widgets inside a bloc provider can access the bloc from the BuildContext and emit events from there. Because the build context is passed down, you can easily access bloc providers deep inside the widget tree.

How about MultiBlocProviders?

A multi-bloc provider is a widget that allows the initialization of multiple bloc instances. When you need to update multiple components at once, you can initialize a multi-bloc provider on the parent widget and then get each of the providers from the context. Retrieving the providers will enable you to send events to the stream, thus updating the components.

Pros and Cons of Using Flutter Bloc

Even though flutter_bloc seems like an obvious choice for your app, you might want to consider the pros and cons. I’ve done my research, and combined with the experience of using it myself and in a team, I think I can give you a broader view.

Pro: Simplicity

The goal of using a third-party library is obviously to simplify the process. Flutter Bloc does just this. You do not have to learn reactive programming concepts as much as you would have to otherwise. The resulting code is readable and manageable.

Pro: Flexible state management

In my opinion, Flutter Bloc is very flexible for the type of problem it is solving. You can trigger state changes easily from the smallest components. All you need is the BuildContext and you’re practically good to go.

Con: Too sophisticated for small apps

Even though it’s simple, I do believe that Flutter Bloc is overkill for small apps. Apps that don’t benefit much from using the BLoC pattern shouldn’t implement Flutter Bloc at all (obviously).

Con: Harder learning curve

The BLoC pattern in and of itself is a hard concept to grasp — let alone Flutter BLoC. BLoC is a different concept altogether. Maybe this point sounds disingenuous because it is a testament to BLoC more than Flutter BLoC. But since a good understanding of BLoC is needed to even comprehend what’s going on, I think it still needs to be said.

Initializing the MultiBlocProvider

First, you have to initialize the multi-bloc provider at the start of the app:

Code snippet by the author

It’s intuitively simple. You only need to initialize the MultiBlocProvider inside the runApp function before anything else. Then create the BlocProviders you want to initialize inside.

Don’t worry about BlocProvider being called before the components are initiated. Because BlocProvider is lazy by default, it will only call when the first event requesting the bloc is called.

Calling the BlocProvider From Context

Now every time you need to call an event, you only need to access the context from inside the bloc. As long as a parent of the widget has been wrapped inside a MultiBlocProvider, you can safely access the bloc this way:

BlocProvider.of<ProductBloc>(context)

Both ProductBloc or SessionBloc can be accessed this way. After adding an event to the stream, the listener will update the UI after it receives its intended state. You can utilize BlocBuilder for this:

Code snippet by the author

You can literally add this anywhere in your app and it will rebuild. Just make sure you are not re-initiating a BlocBuilder of the same bloc inside of it.

Why Do We Need Multi-Bloc Providers?

We need multi-bloc providers to solve problems related to updating multiple components with different states all at the same time. I know it sounds so close to the underlying premise of using multi-bloc providers in the first place and that I am repeating myself. But hear me out.

A great example would be a mobile marketplace app where you would need to search for products while also fetching the product if it is in your cart. Because you have two different events, you need to have two bloc providers.

Wait, isn’t it possible to merge them both together in one event state? Yes, but you would have to get products every time you add an item to your cart. The bloc would lose its atomicity.

You also can’t call two events that return to different states in succession. For example:

BlocProvider.of<ProductBloc>(context)
..add(GetProduct())
..add(GetCart())

It is possible to add a Cart event on the product bloc and call it after you retrieve the product. But because another event is called after GetProduct, the listener that previously loaded product data is also triggered and the state changes.

One way to solve this problem while still getting the atomicity we need is by creating multiple blocs and calling in succession:

BlocProvider.of<ProductBloc>(context)
..add(GetProduct())
BlocProvider.of<CartBloc>(context)
..add(GetCart())

This way, we can separate the BlocBuilder of both the ProductBloc and CartBloc to different components.

Final Words

Creating reactive apps has never been easy. But with Flutter and BLoC, it is completely possible to have a smooth reactive app. You don’t need to worry about your components not updating when they are supposed to and you can increase the UX quality of your app. Nothing says good UX like a reactive app.

In the end, user experience is key. A great user experience is naturally followed by more user engagement. Win users not only by marketing your product but also by giving them the best experience imaginable.

--

--