useFuture
Listens to a Future and exposes its current state as an AsyncSnapshot. It is the hook equivalent of the FutureBuilder widget.
Widget buildProfile(BuildContext context, Future<Profile> request) {
final snapshot = useFuture(request); // <- Listens to the Future, rebuilds on completion
if (snapshot.hasError) return const Text('Failed to load');
final profile = snapshot.data;
return Text(profile?.name ?? 'Loading...'); // <- null until the Future completes
}
Signature
AsyncSnapshot<T> useFuture<T>(Future<T>? future, {T? initialData, bool preserveState = true});
T? useFutureData<T>(
Future<T>? future, {
T? initialData,
bool preserveState = true,
void Function(Object, StackTrace)? onError,
});
future- theFutureto listen to. Anullfuture leaves the snapshot in its initial state.initialData(nullby default) - the value to expose before theFuturecompletes. When provided, the initial snapshot carries it withConnectionState.none.preserveState(trueby default) - when thefutureinstance changes, keep the last value during the gap before the new one resolves. Set it tofalseto reset toinitialDatainstead.
useFutureData is a convenience variant that returns data directly instead of the whole snapshot. It pipes the snapshot through useAsyncSnapshotErrorHandler under the hood, so an emitted error is reported rather than silently swallowed; the optional onError chooses where it goes.
Use cases
- Querying data from an API or database (however, take a look at
useAutoComputedStatewhich is usually better suited for loading screen data - it tracks initialization, guards null inputs, and exposesrefresh). - Resolving a one-shot asynchronous value during a build, such as a file read or a plugin call.
Caveats
-
A new
futureinstance restarts the subscription. Building theFutureinline creates a fresh one on every build, so the work runs again each rebuild. PairuseFuturewithuseMemoized- or reach foruseMemoizedFuture, which packages exactly that:Profile? useProfile(UserService service, String userId) {// Without useMemoized a fresh Future would be created on every build, so the// subscription would restart each rebuild.final request = useMemoized(() => service.loadProfile(userId), [userId]);return useFutureData(request); // <- Returns the value directly; reports errors itself} -
An error from the
Futurelands insnapshot.errorand stays there silently unless something reads it. Handle it explicitly viauseAsyncSnapshotErrorHandler, or useuseFutureData, which wires that handler for you. -
initialDatais read only once, on first build. Later changes to the value passed asinitialDataare ignored, exactly as withuseState.
For loading screen data you usually want useAutoComputedState rather than useFuture + useMemoized. Reach for useFuture when you need raw AsyncSnapshot semantics or are mirroring an existing FutureBuilder.
See also
- useMemoizedFuture -
useFuturepaired withuseMemoizedin a single call - useStream - the same pattern for a
Stream - useAsyncSnapshotErrorHandler - surface errors carried in an
AsyncSnapshot - Common hooks -
useFuture/useStreamin the context of a screen