Skip to content

Core Dependent Queries

Dependent queries are queries that should only execute after some other data is available.

Use the enabled option in QueryOptions to delay execution until a condition is met.

// 1. Fetch the User first
final userQuery = QueryBuilder<User>(
queryKey: 'user'.toQueryKey(),
/* ... */
);
// 2. Fetch Posts *only* when User ID is available
QueryBuilder<List<Post>>(
// Key depends on user ID
queryKey: 'posts:${userQuery.state.data?.id}'.toQueryKey(),
queryFn: () => api.fetchPosts(userQuery.state.data!.id),
// Disable until we have the ID
options: QueryOptions(
enabled: userQuery.state.hasData, // <--- The magic switch
),
builder: (context, state) {
if (state.isIdle) return Text('Waiting for user...');
/* ... */
}
)
OptionTypeDescription
enabledboolSet to false to prevent the query from fetching automatically. Defaults to true.

Sometimes you need to chain requests A -> B -> C.

// This pattern typically involves nesting QueryBuilders
// or using the result of one query to drive the next.
QueryBuilder<User>(
queryKey: 'user'.toQueryKey(),
builder: (context, userState) {
if (!userState.hasData) return Loading();
return QueryBuilder<List<Project>>(
// Dependent key
queryKey: 'projects:${userState.data.id}'.toQueryKey(),
queryFn: () => fetchProjects(userState.data.id),
// Only run when user is loaded (redundant if nested, but good practice)
options: QueryOptions(enabled: userState.hasData),
builder: (context, projectState) {
// ...
}
);
}
)

Don’t search until the user types something.

QueryBuilder(
queryKey: 'search:$searchTerm'.toQueryKey(),
queryFn: () => searchApi(searchTerm),
options: QueryOptions(
enabled: searchTerm.isNotEmpty, // Only fetch if text exists
),
/* ... */
)

Lifecycle Dependencies (Cascading Cancellation)

Section titled “Lifecycle Dependencies (Cascading Cancellation)”

While enabled controls when a query starts, the dependsOn parameter controls when a query stops.

Use dependsOn to link a child query to a parent query. If the parent query is disposed (removed from memory), the child query is automatically cancelled. This prevents orphan requests from consuming resources when their conceptual “owner” is gone.

// Parent Query (e.g., User Profile)
final userKey = 'user:123'.toQueryKey();
final userQuery = client.getQuery(userKey, /* ... */);
// Child Query (e.g., User Posts)
client.getQuery(
'user:123:posts'.toQueryKey(),
queryFn: () => api.fetchUserPosts(123),
// 🔗 Link to parent
dependsOn: userKey,
);
  • Detail Views: A “User Details” query should depend on the “Users List” query if logically nested.
  • Drill-downs: Order Items should depend on Order.
  • Resource ownership: If Query B makes no sense without Query A existing, link them.
  1. Parent Disposed: When the parent query hits 0 subscribers and its garbage collection timer expires.
  2. Cascade: The QueryClient detects the disposal and immediately runs .cancel() on all registered children.
  3. Effect: Any in-flight network requests for the children are aborted. The children are not removed from cache immediately (they follow their own GC logic), but their fetching is stopped.