Flutter: BLoC vs Cubit

Pablo Reyes
4 min readOct 4, 2021

¿Should I use BLoC or Cubit? 🤔 This question can be pretty difficult to answer and the main reason for this, it’s because we are not clear about the benefits of each one. Many people think that because they use BLoC in a project they don’t need cubit, some others think that because BLoC is a “new version” of Cubit then we should not use it. The truth behind these thoughts is pretty different. Let’s describe here why:

Async* / Async / Sync

In short, we can say BLoC is async* and Cubit can be sync or async.

BLoC

When we use BLoC, there’s two important things: “Events” and “States”. That means that when we send an “Event” we can receive one or more “States”, and these “States” are sent in a stream.

Pros

  • We have two streams here: Events and States; and that means that all things that we can apply to a Stream we can do it here. Transformations.
  • We can do long duration operations here, things like: API calls, database, compressions or some other complex things.

Con

  • If you send a lot of events for one BLoC could be difficult to track if you are not tracking them correctly.
  • Sometimes we want to receive this “state” synchronously, but with BLoC this is not possible.

Cubit

When we use cubit, we can only send “states” and to trigger these states we can do it by calling a function (like actions):

Pros

  • We can decide if we want to send these “states” in a synchronous or asynchronous way.
  • We still can do long duration operations here but using async not async* (stream), if you want to use a stream you need to implement more things or move up to BLoC.

Con

  • We don’t have “Events” here, and these are helpful in different situations.
  • We don’t have streams, so no transformations.

Note: BLoC uses mapToState and gives you a stream (async*). Cubit uses normal functions and gives you the result immediately or a Future (with async).

Track Events and States

Sometimes you want to track what “event/state” was sent, or maybe the relation between an “event” and the “state”. This helps us to make the debugging easier, understand the relation between event and state, verify the sequence of the data flow, and check transitions. For large applications sometimes could be difficult to know which “event” triggers certain “states” also report this thing could be useful for analytics.

BLoC: One of the major difference between BLoC and Cubit is this:

“BLoC is Event-Driven and Cubit not.”

Track what event triggers certain states is crucial for predict and check if that matches with the expected result.

BLoC: Because it is event-driven we can know what event it triggered, what’s the current state and the next one. We can override “onTransition” and check how these events are coming and how these states change; another way is using a BLoC observer.

Cubit: As we know, cubit is not event-driven. We call functions (like actions) to send these “states”, so we can track which state were emitted and not events because here there are not events. We can track state overriding the “onChange” function; another way is using a BLoC observer.

Before you go with Cubit or BLoC should think first what you want to do. ¿Will I’m going to handle authentication stuff here? ¿or create a custom toggle? ¿maybe handle a simple UI state?

¿When we should use Cubit?

  • When you want to send something synchronous.
  • When you don’t need to keep track of event-state transitions. You only want state tracking or track nothing (cubit is not event-driven).
  • You need do to something pretty simple. Like a custom toggle, a count-down timer, keep a state of a bar with multiple options. Keep in mind that if the things inside this cubit start become complex probably you should think of migrating it to BLoC (which shouldn’t be too difficult).

¿When we should use BLoC?

  • When you want to track event-state transitions. Helps for debugging, analytics, etc. For example, to know the order of how the events were sent.
  • When you send “Events” from different places. For example, you have multiple BLoC listeners and inside them you send multiple events to another BLoC.
  • You want to do some event transformation. Like buffer, throttle, debounceTime, etc. Think of all transformations that can be applied to a stream.

Additional Note

For things like API Calls, Database Access, etc. you can use BLoC or Cubit, both works, but the thing here is what else do you want to do, is not the same thing send “Events” in a Stream (in BLoC) than call a simple function (in Cubit).

Create a Cubit is easier compared to create a BLoC, that’s why it is also easier to understand it compared to BLoC, so if you have some doubts about if it is necessary to create a BLoC or Cubit, then you can go with a Cubit and later migrate it to BLoC, but my advice is always try to think about the “When we should use BLoC/Cubit” hopefully you will get some clues of what’s the right path.

Also, maybe you can say: “Hey Pablo but I don’t want to keep track of transitions, I don’t need transforms, also I don’t care about events, can I just use Cubit and not BLoC?”… well in that case you can go always with cubit for Authentication, API calls, and all that stuff, but keep in mind that the reality is different, in a real big projects you will need BLoC in a lot of cases, because big products always require keeping track of things, add analytics, transform complex things, reduce debugging time, etc … however in these same big projects, Cubit also will play an important role to implement simple things.

--

--