future property
Exposes a Future which resolves with the last value or error emitted.
This can be useful for scenarios where we want to read the current value exposed by a StreamProvider, but also handle the scenario were no value were emitted yet:
final configsProvider = StreamProvider<Configuration>((ref) async* {
// somehow emit a Configuration instance
});
final productsProvider = FutureProvider<Products>((ref) async {
// If a "Configuration" was emitted, retrieve it.
// Otherwise, wait for a Configuration to be emitted.
final configs = await ref.watch(configsProvider.future);
final response = await httpClient.get('${configs.host}/products');
return Products.fromJson(response.data);
});
Why not use StreamProvider.stream.first
instead?
If you are familiar with streams, you may wonder why not use Stream.first instead:
final configsProvider = StreamProvider<Configuration>((ref) {...});
final productsProvider = FutureProvider<Products>((ref) async {
final configs = await ref.watch(configsProvider.stream).first;
...
}
The problem with this code is, unless your StreamProvider is creating
a BehaviorSubject
from package:rxdart
, you have a bug.
By default, if we call Stream.first after the first value was emitted, then the Future created will not obtain that first value but instead wait for a second one – which may never come.
The following code demonstrate this problem:
final exampleProvider = StreamProvider<int>((ref) async* {
yield 42;
});
final anotherProvider = FutureProvider<void>((ref) async {
print(await ref.watch(exampleProvider.stream).first);
// The code will block here and wait forever
print(await ref.watch(exampleProvider.stream).first);
print('this code is never reached');
});
void main() async {
final container = ProviderContainer();
await container.read(anotherProvider.future);
// never reached
print('done');
}
This snippet will print 42
once, then wait forever.
On the other hand, if we used future, our code would correctly execute:
final exampleProvider = StreamProvider<int>((ref) async* {
yield 42;
});
final anotherProvider = FutureProvider<void>((ref) async {
print(await ref.watch(exampleProvider.future));
print(await ref.watch(exampleProvider.future));
print('completed');
});
void main() async {
final container = ProviderContainer();
await container.read(anotherProvider.future);
print('done');
}
with this modification, our code will now print:
42
42
completed
done
which is the expected behavior.
Implementation
AlwaysAliveProviderBase<Future<State>> get future =>
AsyncValueAsFutureProvider(this, from: from, argument: argument);