How to work around flutter_bloc breaking changes
At Lumen, we use one of the most common patterns for state management called BLoC, which allows for relatively easy implementation of UI logic as well as testing. BLoC, short for Business Logic Component, was introduced by Google back in 2018 to help convert events to state during the user interaction.
There are several ways to introduce this pattern to your Flutter code, and we’ve chosen the flutter_bloc package, which combines the BLoC package with the provider package. By using a specialized provider widget called BlocProvider, flutter_bloc allows for easy dependency injection of your BLoCs in the widget tree.
But let’s back up for a moment. What is state management? In its simplest form, state management is the process of managing one or more user interface controls (i.e., text fields, buttons, etc.). In other words, it’s a way of managing the behavior of an app at any given moment in time.
Unfortunately, while using the flutter_bloc package, we came across several updates that broke our implementation and the behavior of the API. Here’s a look at how we chose to tackle those changes.
Breaking change in v6 : Streams
This issue was introduced in version 6.0 when flutter_bloc synced its streams implementation to the one used in the BLoC package. Versions prior to this one used to handle streams in the same manner as an rxdart BehaviorSubject would, so when a listener attached to a stream, it would immediately get the last element on the stream. The new version, however, changed that behavior to be like a PublishSubject in which a listener only gets new elements that are added to the stream. This change affected BlocBuilder, BlocListener and BlocConsumer widgets.
Consider the following code sample:
This sample behaves differently before and after flutter_bloc 6.0. Before 6.0, the listen would be called immediately with the current state of myBloc, but now it is only called if state changes after the listener’s registration. Obviously, this broke our app in many places, and we needed a solution for it. So, we created the following flutter extension that makes the code work as it did before:
Note: This code is compatible with rxdart 24. Version 25 is using a MergeStream class to perform the same operation.
Our solution is to build an extension that uses rxdart’s merge, which creates a new stream out of the current state of the BLoC and the BLoC’s stream. This way, we will immediately get the current state followed by any future state changes to the BLoC.
To keep the solution even easier to use and compatible with the previous BLoC implementation, we have added a mergedListen method, which listens to the merged stream and lets you invoke your existing stream manipulation methods.
Consideration when updating to v5 : Initial state
Due to the limitations imposed by dart, the conversion from a getter method to an initial state constructor parameter, we had to find a solution for some use cases. One such case is the need to do some prior calculation to determine the initial state, but this can be solved by using a static method as shown in the example below. Eventually, the solution to this change depends greatly on the way your BLoC is implemented, so there is no “fits all solution” in this case.
Conclusion
Like many packages in the evolving flutter world, flutter_bloc moves at a fast pace and may break things along the way. Version 6.1.0 already requires more API changes to the context extension, which makes context.BLoC() deprecated. It can be challenging at times to keep up, but we are all on the path to a better platform in the not too distant future.