Engineering

A deep dive into riverpod vs bloc

| 4 min read

Lately, Riverpod, a “better version of provider” created by no one else than the original creator of provider Remi Rousselet, has been a hot topic in the Flutter community. It solves many problems of provider package and adds additional features that the provider wasn’t capable of doing, but how does it compare to a bloc? How does it manage the state? How about dependency injection? Is it testable in the same way as a bloc is? I’ll try to answer all of these while going in-depth on how riverpod works compared to bloc.

This article focuses on riverpod. Note that I won’t be explaining how bloc works. So it’d be better if you had at least basic knowledge of bloc before reading this.

State management

Ok, let’s start with a simple fetch data example Cubit like this:

Riverpod has something very similar to Cubit, called StateNotifier. When you look at the code, it’s nearly identical:

StateNotifier is just a Cubit. The implementation of both is very similar, and both are based on StreamController, an already proven concept in real-world applications.

Ok, but what about widget level? What’s the difference there?

Well, little to no difference, riverpod offers similar .watch, .read methods. It also provides a modifier similar to .select. Ok, enough talking, let’s see the widget:

*ConsumerWidget acts just as a StatelessWidget, with a handy way to access your providers with WidgetRef.

There’s also Consumer as a widget that you can use inside your widget tree (similar to BlocBuilder). With it, you can more easily decide which parts of the UI should be rebuilt or used inside a StatefulWidget.

Like BlocListener, there’s a .listen method that listens to the state’s changes without rebuilding the widget, just without nesting another widget in your widget tree.

You can also wrap futures, streams, or even singular values into providers and use them in your widgets with the same WidgetRef logic, which can turn out pretty handy, especially if you want to test these.

Dependency Injection

Bloc, highly inspired by a provider, uses a simple DI. It consists of the same issues that riverpod tries to solve. Riverpod replaces BlocProviders and RepositoryProviders with, you guessed it, “providers.”

To make it work, you need to wrap your app with ProviderScope, and that’s it. ProviderScope works exactly like RepositoryProvider, it creates objects when they’re listened to.

Because ProviderScope is localized on top of the widgets tree, disposing of the dependencies is not easy. Usually, you don’t want to keep the state of the particular widget through its lifetime, and you wish to auto-dispose it once it’s closed. You can do it by adding a modifier to the StateNotifierProvider like this:

It’s also worth mentioning that when creating a provider, you get access to the ref, with which you can access other dependencies like in the example above. If they are not initialized by the point of accessing them, they will be initialized then.

Unit tests

Testing the state notifier is pretty straightforward. Although it doesn’t provide an additional library for tests like bloc, nothing more than the standard flutter test library and mockito/mocktail should be enough.

Widget or golden tests

Widget and golden tests are another story because they depend on your approach. At AppUnite, we usually try to mock the lowest layer we can, so we ensure to test only the UI and not everything in between. It also gives us more flexibility. And here is the problem - you can’t mock state notifiers’ state (or at least at the point of writing this article, more info here).

You can mock different layers, like in the example above, and test your widgets this way, but it’s imperfect as you don’t completely control what you’re displaying.

Creating a wrapper class for StateNotifier and setting the desired state directly there would be another solution.

Summary

To wrap up, riverpod is a great state management package that, on many occasions, is simpler than bloc. It provides a similar level of documentation, and many people are starting to pick it up in their real-world applications. The dependency injection is of a much higher standard than bloc’s. The syntax and feel of riverpod are much better than bloc’s, and once you learn it, it becomes a joy to write the code.

But not everything’s perfect. Golden tests are a big part of our development, and utilizing them to their full potential is very important to us. If it’s not valuable to you, there’s not much to say against riverpod.

If you want to see the complete code used in this article, there’s a link to the GitHub repository.