fasq_bloc 0.3.1
fasq_bloc: ^0.3.1 copied to clipboard
Bloc/Cubit adapter for FASQ (Flutter Async State Query) - async state management with Bloc
fasq_bloc #
The Native Bloc Adapter for FASQ.
Bring the power of caching, optimistic updates, and offline support to your Bloc application with zero friction. fasq_bloc bridges the gap between fasq's powerful query engine and the flutter_bloc ecosystem, allowing you to build complex data-driven apps without fighting your architecture.
Current Version: 0.3.0
π Documentation #
For full documentation and API reference, visit:
https://fasq.shafi.dev/adapters/bloc
β¨ Features #
- π§ Zombie-proof Caching: Data is cached, deduplicated, and garbage collected automatically. No more stale data bugs.
- π‘οΈ Resilience Built-in: Circuit Breakers protect your app from crashing backends.
- π Offline-First: Queue mutations when offline and sync automatically when connectivity returns.
- π Native Feel: Zero friction. It's just a
CubitwhereQueryClientis auto-injected. - π§© Composition: Solve "Bloc Hell" by composing multiple queries in a single Bloc with
FasqSubscriptionMixin.
π¦ Installation #
dependencies:
fasq_bloc: ^0.3.0
flutter_bloc: ^8.0.0
π Quick Start #
1. Setup the Provider #
Wrap your app (or feature) with FasqBlocProvider. It automatically handles the QueryClient lifecycle.
void main() {
runApp(
FasqBlocProvider(
child: MyApp(),
),
);
}
2. Create a QueryCubit #
Extend QueryCubit for a 1-to-1 mapping between a Bloc and a Query.
class UserCubit extends QueryCubit<User> {
final int userId;
UserCubit(this.userId);
@override
QueryKey get queryKey => QueryKey('user', args: userId);
@override
Future<User> Function() get queryFn => () => api.fetchUser(userId);
}
3. Use in UI #
Consume it like any other Bloc.
BlocBuilder<UserCubit, QueryState<User>>(
builder: (context, state) {
if (state.isLoading) return CircularProgressIndicator();
if (state.hasData) return Text('Hello ${state.data!.name}');
return Text('Error: ${state.error}');
},
)
π οΈ Advanced Usage #
Composition (Multiple Queries) #
Need to fetch a User and their Posts in the same Bloc? Use the FasqSubscriptionMixin.
class DashboardBloc extends Cubit<DashboardState> with FasqSubscriptionMixin {
DashboardBloc() : super(DashboardState.loading()) {
// 1. Fetch User
final userQuery = client.getQuery<User>('user'.toQueryKey(), ...);
subscribeToQuery(userQuery, (state) {
// Update internal state based on User query
emit(state.copyWith(user: state.data));
});
// 2. Fetch Posts
final postsQuery = client.getQuery<List<Post>>('posts'.toQueryKey(), ...);
subscribeToQuery(postsQuery, (state) {
emit(state.copyWith(posts: state.data));
});
}
}
Optimistic Updates #
Update your UI before the server responds for a snappy experience.
class TodosCubit extends MutationCubit<Todo, String> {
// ... configuration ...
void addTodo(String text) {
mutate(
text,
onMutate: () async {
// 1. Cancel outgoing refetches
await queryClient.cancelQueries('todos');
// 2. Snapshot previous value
final previous = queryClient.getQueryData<List<Todo>>('todos');
// 3. Optimistically update cache
queryClient.setQueryData<List<Todo>>(
'todos',
[...previous ?? [], Todo(id: 'temp', text: text)]
);
return { 'previous': previous };
},
onError: (error, context) {
// 4. Rollback on error
queryClient.setQueryData('todos', context['previous']);
},
onSettled: () {
// 5. Always refetch to ensure sync
queryClient.invalidateQueries('todos');
}
);
}
}