Skip to content

QueryCubit

The QueryCubit is an abstract base class that wraps a Fasq query, emitting QueryState changes. Extend QueryCubit to create your own query cubits with type-safe, structured state management.

Extend QueryCubit and implement the required getters:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fasq_bloc/fasq_bloc.dart';
class UsersQueryCubit extends QueryCubit<List<User>> {
@override
QueryKey get queryKey => 'users'.toQueryKey();
@override
Future<List<User>> Function() get queryFn => () => api.fetchUsers();
}
class UsersScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Users')),
body: BlocProvider(
create: (context) => UsersQueryCubit(),
child: BlocBuilder<UsersQueryCubit, QueryState<List<User>>>(
builder: (context, state) {
if (state.isLoading) {
return Center(child: CircularProgressIndicator());
}
if (state.hasError) {
return Center(child: Text('Error: ${state.error}'));
}
if (state.hasData) {
return ListView.builder(
itemCount: state.data!.length,
itemBuilder: (context, index) {
final user = state.data![index];
return ListTile(title: Text(user.name));
},
);
}
return Center(child: Text('No users found.'));
},
),
),
);
}
}

Configure query behavior by overriding the options getter:

class UsersQueryCubit extends QueryCubit<List<User>> {
@override
QueryKey get queryKey => 'users'.toQueryKey();
@override
Future<List<User>> Function() get queryFn => () => api.fetchUsers();
@override
QueryOptions? get options => QueryOptions(
staleTime: Duration(minutes: 5),
cacheTime: Duration(minutes: 10),
enabled: true,
onSuccess: () {
print('Users fetched successfully');
},
onError: (error) {
print('Error fetching users: $error');
},
);
}

Cancel an in-flight query without closing the Cubit. This is cooperative cancellation, meaning your queryFn must check the cancellation token.

context.read<UsersQueryCubit>().cancel();

You can manually set the data for a query immediately. This is useful for optimistic UI patterns or syncing local state.

final newUser = User(id: '123', name: 'New User');
context.read<UsersQueryCubit>().setData([newUser]);

Update query options at runtime (e.g., enable/disable fetching, change stale time). This will automatically swap the underlying query if necessary.

// Pause fetching
context.read<UsersQueryCubit>().updateOptions(
newOptions: QueryOptions(enabled: false),
);
// Resume fetching with new stale time
context.read<UsersQueryCubit>().updateOptions(
newOptions: QueryOptions(
enabled: true,
staleTime: Duration(seconds: 30),
),
);

Use constructor parameters or fields for dynamic query keys:

class UserProfileQueryCubit extends QueryCubit<User> {
final String userId;
UserProfileQueryCubit(this.userId);
@override
QueryKey get queryKey => 'user:$userId'.toQueryKey();
@override
Future<User> Function() get queryFn => () => api.fetchUser(userId);
}

Disable queries based on conditions using the enabled option:

class UserProfileQueryCubit extends QueryCubit<User?> {
final String? userId;
UserProfileQueryCubit(this.userId);
@override
QueryKey get queryKey => 'user:$userId'.toQueryKey();
@override
Future<User?> Function() get queryFn => () => api.fetchUser(userId!);
@override
QueryOptions? get options => QueryOptions(
enabled: userId != null,
);
}

Trigger manual refetches using the Cubit:

context.read<UsersQueryCubit>().refetch();