Bloc and Cubit Overview

Cubit vs Bloc

Both Bloc and Cubit are objects that encapsulate application state. More precisely a Flow of application states. The difference between them is that in order to change the application state, with Bloc the developer need to send Events to the Bloc, while with Cubit the developer just call methods of the specific Cubit class.

So basically with Bloc there is an additional level of abstraction (Events) separating the request of changing application state and the actual change, that many times can be useful. But many other times the simpler approach of a Cubit can be preferable. Let's see an example of implementing the same functionality both with a Cubit and a Bloc

From the point of view of the UI code that is "consuming" the state of the Bloc or Cubit, there is no difference. Both Bloc and Cubit expose both a field state with the current state value, and a stream field that is a StateFlow<BlocState>, that can be converted to a state that can trigger UI recomposition in Compose with the method collectAsState . But you don't actually need to do it manually because compose_bloc provides higher level methods like BlocBuilder, that do it for you.

onChange and onError

In addition to subscribing to the Flow of states associated to a specific Cubit or Bloc instance, it is also possible to listen to state changes in other ways, both for a specific instance but also globally for all Bloc and Cubit instances. This can be useful for example for debugging. Let's see how this is done. For example for the CounterCubit class we defined above we can write:

where Change is defined as

It is also possible to observe "errors": every Cubit has an addError method to indicate that an error has occurred while handling a state change for a specific Cubit instance:

overriding onChange and OnError is also available for Blocs.

OnTransition (for Blocs only)

For Blocs there is an additional method that can be overridden that is similar to onChange but that also provides the information about the specific event that caused the state changes:

where Transition is defined as:

onEvent (for Blocs only)

For Blocs there is an additional method that can be overridden that is called every time a new event is received. Note that not all events will cause emission of a new Bloc state, so onEvent is not equivalent to OnTransition

Observing Cubits and Blocs globally

It is also possible to observe for state changes and for error in processing state changes globally for all Blocs and Cubits with a BlocObserver and BlocOverrides. This is very useful for debugging.

With a global BlocObserver we can observe not only state changes for Blocs and Cubits but also many other events. Here is the full definition of the BlocObserver interface:

Event Transformation (for Blocs only)

With Bloc we can provide a custom EventTransformer to change the way incoming events are processed by the Bloc. An EventTransformer is defined as

For example we can define an EventTransformer for debouncing events:

we can set EventTransformer at level of the single BlocEventHandler but also globally using BlocOverrides.runWithOverrides.

There are some standard eventtransformer ready to be used, in package

Last updated

Was this helpful?