Reputation: 2292
Imagine I've got a (contrived) state that looks something like:
class UserState {
final String? name;
final String? address;
}
The source for name
comes from a single (asynchronous) API call, but the address
field value comes from a stream. Conceptually, the bloc might emit the following:
UserState(name: null, address: null)
UserState(name: null, address: 'New York')
UserState(name: 'john', address: 'New York')
UserState(name: 'john', address: 'Smith St, New York')
UserState(name: 'john', address: '10 Smith St, New York')
The screen that receives these states is perfectly capable of rendering itself based on the presence (or not) of name
and address
.
I can see that there's emit.forEach()
that looks like it would be perfect for dealing with the stream, but I'm wrestling with how to deal with awaiting for the fetchName()
API call.
I thought of having a local variable which keep track of whether name
has came back (something like the following) but there's a scenario where fetchName()
is slow and doesn't return until after all the address stream events have finished - which means I miss a state.
on<FetchUserEvent>((event, emit) async {
String? name;
fetchName().then((n) => name = n);
emit.forEach(
addressStream,
onData: (a) => UserState(name: name, address: a)
);
});
Is my only option to have to import RxDart just to pick up something like "combine latest"?
Am I "doing it wrong"?
Upvotes: 2
Views: 3083
Reputation: 190
The two calls should be separated into different events. Bloc dispatches events concurrently by default, so the following should work:
In the bloc:
on<FetchUserEvent>((event, emit) async {
final name = await fetchName();
emit(UserState(name: name, address: state.address));
});
on<ListenAddressEvent>((event, emit) async {
await emit.forEach(
event.addressStream,
onData: (newAddress) => UserState(name: state.name, address: newAddress),
);
});
And the bloc provider widget dispatches the two events eg. on create:
BlocProvider(
create: (context) => UserBloc()
..add(FetchUserEvent())
..add(ListenAddressEvent()),
);
You need to use await
before emit.forEach
, otherwise it won't really listen. It throws an error about it if you forget as far as I remember.
Just to make sure: in this example the FetchUserEvent
and the ListenAddressEvent
events will start together if you use at least bloc 8.0.
Upvotes: 0
Reputation: 2729
on<FetchUserEvent>((event, emit) async {
String? name = await fetchName();
emit.forEach(
addressStream,
onData: (a) => UserState(name: name, address: a)
);
});
Upvotes: 0