Problem

For my latest project I decided to use BLoC pattern. I would not say I am a big fan of it, but it seems it on the way to become the most popular state management solution in Flutter.

And when I started to work on remote request to the server (well, making stubs with simulating delayed response), I started to think about how to align BLoC with the way I handled the remote request in my React/Mobx applications.

In my old applications, this type of code will look something like this:

The things I do not like with this approach:

  1. You always have to couple the value you are getting remotely with the pending state.
  2. The pending state cannot handle errors. For example, if your request failed, you are no longer in the pending state but what about the state of your object? From example above, if you were not able to get the list of the project and the array is empty now, is it because there were no projects, or it was because of the error? So now you have to find ways of handling errors, either in components or by adding more properties to your service.

One way to approach it could be:

But, if it is more or less clear with “pending” and “success” states, what about “error”? The “projects” property will always be an array, either empty or not, but what about the error? Where to store the error in case of… well, error? Another property?

In more flexible languages like Rust, enums can hold values associated with it, but unfortunately, it is not the case for Dart.

With BLoC, this approach starts to become even more problematic. Now the “projects” is not an array but StreamController or for example, BehaviorSubject, if ok with RxDart:

But in this case, what should we do with “isPending” or “state” property? Should it also be a stream? I that case we will need to listen to two streams? Or, even though those properties are tightly coupled with each other, one will be normal variable and other will be a stream?

Solution

And here, we have another cool thing we can steal from Rust which is “Result” pattern! Well, maybe not from Rust, but because Rust does not have NULL this approach is so popular. Even more, Dart also have it in flutter’s async library!

The idea is simple. Instead haveing pointer to projects array, we are storing the Result object which like the one below:

As I mentioned before, Flutter already has this class, and also, ValueResult and ErrorResult inherited from it. What we can do, is to extend ValueResult for Success and Pending states like:

And, ErrorResult was already made for us

So, now if we successfully was able to load our project, we just wrap the array into SuccessResult or wrap an exception in case of error like below:

In my opinion, state management looks much cleaner now and all the states relevant to the projects are in one place. Plus, this approach is quite flexible, so for more states you can just introduce new Result types. But, there is one caveat though which I will explain below.

Using with Widgets

But for now, lets use our updated stream in StreamBuilder:

The catch!

Here is the catch! You probably naturally thought about poking me in my eye for all those “else if” statement right? The one below would look much cleaner, right?:

Unfortunately, this is another Dart’s limitation. While “is” operator would return true for “data is SuccessResult” and “data is SuccessResult<List<Propect>>”, this is not the case for “switch”.

I our specific case, our runtimeType property will SuccessResult<List<Propect>> which cannot be used as “case” value, so:

and

Conclusion

I am still experimenting with this approach and not trying to convince you to use it. So far I am really happy with code readability I am getting with it and just wanted to share it so someone else maybe would also want to try it.

Passionate Software Developer with a strong focus on Web, 3D, Mobile, and pretty much any interactive computer graphics.