FAQs
❔ Question: I’m emitting a state in my bloc but the UI is not updating. What am I doing wrong?
💡 Answer: If you’re using Equatable make sure to pass all properties to the props getter.
✅ GOOD
❌ BAD
In addition, make sure you are emitting a new instance of the state in your bloc.
✅ GOOD
❌ BAD
❔Question: When should I use Equatable?
💡Answer:
In the above scenario if StateA
extends Equatable
only one state change will
occur (the second emit will be ignored). In general, you should use Equatable
if you want to optimize your code to reduce the number of rebuilds. You should
not use Equatable
if you want the same state back-to-back to trigger multiple
transitions.
In addition, using Equatable
makes it much easier to test blocs since we can
expect specific instances of bloc states rather than using Matchers
or
Predicates
.
Without Equatable
the above test would fail and would need to be rewritten
like:
❔ Question: How can I handle an error while still showing previous data?
💡 Answer:
This highly depends on how the state of the bloc has been modeled. In cases where data should still be retained even in the presence of an error, consider using a single state class.
This will allow widgets to have access to the data
and error
properties
simultaneously and the bloc can use state.copyWith
to retain old data even
when an error has occurred.
❔ Question: What’s the difference between Bloc and Redux?
💡 Answer:
BLoC is a design pattern that is defined by the following rules:
- Input and Output of the BLoC are simple Streams and Sinks.
- Dependencies must be injectable and Platform agnostic.
- No platform branching is allowed.
- Implementation can be whatever you want as long as you follow the above rules.
The UI guidelines are:
- Each “complex enough” component has a corresponding BLoC.
- Components should send inputs “as is”.
- Components should show outputs as close as possible to “as is”.
- All branching should be based on simple BLoC boolean outputs.
The Bloc Library implements the BLoC Design Pattern and aims to abstract RxDart in order to simplify the developer experience.
The three principles of Redux are:
- Single source of truth
- State is read-only
- Changes are made with pure functions
The bloc library violates the first principle; with bloc state is distributed across multiple blocs. Furthermore, there is no concept of middleware in bloc and bloc is designed to make async state changes very easy, allowing you to emit multiple states for a single event.
❔ Question: What’s the difference between Bloc and Provider?
💡 Answer: provider
is designed for dependency injection (it wraps
InheritedWidget
). You still need to figure out how to manage your state (via
ChangeNotifier
, Bloc
, Mobx
, etc…). The Bloc Library uses provider
internally to make it easy to provide and access blocs throughout the widget
tree.
❔ Question: When using BlocProvider.of(context)
it cannot find the bloc.
How can I fix this?
💡 Answer: You cannot access a bloc from the same context in which it was
provided so you must ensure BlocProvider.of()
is called within a child
BuildContext
.
✅ GOOD
❌ BAD
❔ Question: How should I structure my project?
💡 Answer: While there is really no right/wrong answer to this question, some recommended references are
The most important thing is having a consistent and intentional project structure.
❔ Question: Is it okay to add events within a bloc?
💡 Answer: In most cases, events should be added externally but in some select cases it may make sense for events to be added internally.
The most common situation in which internal events are used is when state changes must occur in response to real-time updates from a repository. In these situations, the repository is the stimulus for the state change instead of an external event such as a button tap.
In the following example, the state of MyBloc
is dependent on the current user
which is exposed via the Stream<User>
from the UserRepository
. MyBloc
listens for changes in the current user and adds an internal _UserChanged
event whenever a user is emitted from the user stream.
By adding an internal event, we are also able to specify a custom transformer
for the event to determine how multiple _UserChanged
events will be processed
— by default they will be processed concurrently.
It’s highly recommended that internal events are private. This is an explicit way of signaling that a specific event is used only within the bloc itself and prevents external components from knowing about the event.
We can alternatively define an external Started
event and use the
emit.forEach
API to handle reacting to real-time user updates:
The benefits of the above approach are:
- We do not need an internal
_UserChanged
event - We do not need to manage the
StreamSubscription
manually - We have full control over when the bloc subscribes to the stream of user updates
The drawbacks of the above approach are:
- We cannot easily
pause
orresume
the subscription - We need to expose a public
Started
event which must be added externally - We cannot use a custom
transformer
to adjust how we react to user updates
❔ Question: Is it okay to expose public methods on my bloc and cubit instances?
💡 Answer
When creating a cubit, it’s recommended to only expose public methods for the
purposes of triggering state changes. As a result, generally all public methods
on a cubit instance should return void
or Future<void>
.
When creating a bloc, it’s recommended to avoid exposing any custom public
methods and instead notify the bloc of events by calling add
.