useQuery<TData, TError> function

QueryResult<TData, TError> useQuery<TData, TError>(
  1. List<Object?> queryKey,
  2. QueryFn<TData> queryFn, {
  3. bool? enabled,
  4. NetworkMode? networkMode,
  5. StaleDuration? staleDuration,
  6. GcDuration? gcDuration,
  7. TData? placeholder,
  8. RefetchOnMount? refetchOnMount,
  9. RefetchOnResume? refetchOnResume,
  10. RefetchOnReconnect? refetchOnReconnect,
  11. Duration? refetchInterval,
  12. RetryResolver<TError>? retry,
  13. bool? retryOnMount,
  14. TData? seed,
  15. DateTime? seedUpdatedAt,
  16. Map<String, dynamic>? meta,
  17. QueryClient? client,
})

A hook for fetching, caching, and subscribing to async data.

This hook manages the complete lifecycle of async data fetching, including request deduplication, caching, background refetching, and stale-while- revalidate patterns.

The queryKey uniquely identifies this query in the cache. Queries with the same key share cached data across the widget tree.

The queryFn fetches the data when the query needs to execute. It receives a QueryFunctionContext with the query key and other metadata.

Returns a QueryResult containing the current state of the query, including data, error, and status flags. The widget rebuilds automatically when the query state changes.

Options

  • enabled: Whether the query should execute. Defaults to true. Set to false to disable automatic fetching.

  • networkMode: The network connectivity mode for this query. Has no effect unless connectivityChanges is provided to QueryClient. Can be NetworkMode.online (default, pauses when offline), NetworkMode.always (ignores network state), or NetworkMode.offlineFirst (first fetch runs immediately, retries pause when offline).

  • staleDuration: How long data remains fresh before becoming stale. Stale data may be refetched on the next access. Defaults to zero (data is immediately stale).

  • gcDuration: How long unused data remains in cache before garbage collection. Defaults to 5 minutes.

  • placeholder: Data to display while the query is pending and has no cached data. Unlike seed, placeholder data is not persisted to the cache.

  • refetchOnMount: Controls refetch behavior when this hook mounts. Can be RefetchOnMount.stale (default), RefetchOnMount.always, or RefetchOnMount.never.

  • refetchOnResume: Controls refetch behavior when the app resumes from background. Can be RefetchOnResume.stale (default), RefetchOnResume.always, or RefetchOnResume.never.

  • refetchOnReconnect: Controls refetch behavior when network connectivity is restored. Can be RefetchOnReconnect.stale (default), RefetchOnReconnect.always, or RefetchOnReconnect.never. Requires connectivityChanges to be provided to QueryClient.

  • refetchInterval: Automatically refetch at the specified interval while this hook is mounted.

  • retry: A callback that controls retry behavior on failure. Returns a Duration to retry after waiting, or null to stop retrying. Defaults to 3 retries with exponential backoff (1s, 2s, 4s).

  • retryOnMount: Whether to retry failed queries when this hook mounts. Defaults to true.

  • seed: Initial data to populate the cache before the first fetch. Unlike placeholder, seed data is persisted to the cache.

  • seedUpdatedAt: The timestamp when seed data was last updated. Used to determine staleness of seed data.

  • meta: A map of arbitrary metadata attached to this query, accessible in the query function context. When multiple hooks share the same query key, their meta maps are deep merged.

  • client: The QueryClient to use. If provided, takes precedence over the nearest QueryClientProvider ancestor.

See also:

Implementation

QueryResult<TData, TError> useQuery<TData, TError>(
  List<Object?> queryKey,
  QueryFn<TData> queryFn, {
  bool? enabled,
  NetworkMode? networkMode,
  StaleDuration? staleDuration,
  GcDuration? gcDuration,
  TData? placeholder,
  RefetchOnMount? refetchOnMount,
  RefetchOnResume? refetchOnResume,
  RefetchOnReconnect? refetchOnReconnect,
  Duration? refetchInterval,
  RetryResolver<TError>? retry,
  bool? retryOnMount,
  TData? seed,
  DateTime? seedUpdatedAt,
  Map<String, dynamic>? meta,
  QueryClient? client,
}) {
  final effectiveClient = useQueryClient(client);

  // Create observer once per component instance
  final observer = useMemoized(
    () => QueryObserver<TData, TError>(
      effectiveClient,
      QueryOptions(
        queryKey,
        queryFn,
        enabled: enabled,
        staleDuration: staleDuration,
        gcDuration: gcDuration,
        meta: meta,
        networkMode: networkMode,
        placeholder: placeholder,
        refetchInterval: refetchInterval,
        refetchOnMount: refetchOnMount,
        refetchOnResume: refetchOnResume,
        refetchOnReconnect: refetchOnReconnect,
        retry: retry,
        retryOnMount: retryOnMount,
        seed: seed,
        seedUpdatedAt: seedUpdatedAt,
      ),
    ),
    [effectiveClient],
  );

  // Mount observer and cleanup on unmount
  useEffect(() {
    observer.onMount();
    return observer.onUnmount;
  }, [observer]);

  // Handle app lifecycle resume events
  useEffect(() {
    final listener = AppLifecycleListener(onResume: observer.onResume);
    return listener.dispose;
  }, [observer]);

  // Update options during render (before subscribing)
  observer.options = QueryOptions(
    queryKey,
    queryFn,
    enabled: enabled,
    staleDuration: staleDuration,
    gcDuration: gcDuration,
    meta: meta,
    networkMode: networkMode,
    placeholder: placeholder,
    refetchInterval: refetchInterval,
    refetchOnMount: refetchOnMount,
    refetchOnResume: refetchOnResume,
    refetchOnReconnect: refetchOnReconnect,
    retry: retry,
    retryOnMount: retryOnMount,
    seed: seed,
    seedUpdatedAt: seedUpdatedAt,
  );

  // Subscribe to observer and trigger rebuilds when result changes
  // Uses useState with useEffect subscription for synchronous updates
  final result = useState(observer.result);

  useEffect(() {
    final unsubscribe = observer.subscribe((newResult) {
      result.value = newResult;
    });
    return unsubscribe;
  }, [observer]);

  return result.value;
}