Riverpod Adapter
The Riverpod adapter provides idiomatic, type-safe providers for Fasq using flutter_riverpod. It simplifies state management by integrating Fasq’s powerful caching and synchronization with Riverpod’s dependency injection and reactive patterns.
Why Fasq + Riverpod?
Section titled “Why Fasq + Riverpod?”While Riverpod is excellent for managing application state, it doesn’t natively handle the complexities of “Server State” (data that lives on a server, is asynchronous, and needs caching). Fasq solves several key problems for Riverpod users:
- Professional Caching: Implements Stale-While-Revalidate (SWR), background refetching, and custom cache expiry (
staleTime) natively. - Simplified Mutations: Provides a dedicated
mutationProviderwith built-in loading/error states and an imperative API (.mutate()). - Offline Sync: Automatically queues mutations while offline and syncs them once the connection is restored.
- Infinite Pagination: High-level abstractions for infinite scrolling and cursor-based pagination that reduce boilerplate.
- Coordinated Invalidation: Easily invalidate specific queries or entire “tags” of data to keep your UI in sync after updates.
- Declarative Retries & Polling: Configure retry logic and polling intervals as simple declarations rather than manual timers.
- Standard Riverpod Experience: Returns
AsyncValue<T>, so it feels exactly like a standard Riverpod provider but with much more power.
Overview
Section titled “Overview”The Riverpod adapter provides:
queryProvider- Creates queries that returnAsyncValue<T>infiniteQueryProvider- Handles paginated data withAsyncValuemutationProvider- Manages server-side operations with imperative callsPrefetchExtension- Extension onWidgetReffor easy prefetching- Full Dependency Injection - Uses
fasqClientProviderfor the underlying client
Installation
Section titled “Installation”Add the adapter and Riverpod to your project:
flutter pub add fasq_riverpod flutter_riverpod[!NOTE] This adapter is built on top of the fasq core package, which will be added automatically as a dependency.
Basic Usage
Section titled “Basic Usage”import 'package:flutter_riverpod/flutter_riverpod.dart';import 'package:fasq_riverpod/fasq_riverpod.dart';
// 1. Define your providerfinal usersProvider = queryProvider<List<User>>( 'users'.toQueryKey(), () => api.fetchUsers(), options: QueryOptions( staleTime: Duration(minutes: 5), ),);
class UsersScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // 2. Watch the provider final usersAsync = ref.watch(usersProvider);
// 3. Handle states using .when() return usersAsync.when( data: (users) => ListView.builder( itemCount: users.length, itemBuilder: (context, index) => ListTile( title: Text(users[index].name), ), ), loading: () => Center(child: CircularProgressIndicator()), error: (error, stack) => Center(child: Text('Error: $error')), ); }}Key Features
Section titled “Key Features”Idiomatic Riverpod
Section titled “Idiomatic Riverpod”Fasq Riverpod providers return Riverpod’s native AsyncValue<T>. This means you can use all the familiar patterns like .when(), .maybeWhen(), and AsyncValue.copyWithPrevious.
Compile-Safe & Type-Safe
Section titled “Compile-Safe & Type-Safe”Everything is strictly typed. If your fetch function returns Future<User>, your provider will be of type AutoDisposeAsyncNotifierProvider<..., User>, and ref.watch will return AsyncValue<User>.
Automatic Refetch & Background Sync
Section titled “Automatic Refetch & Background Sync”The providers automatically handle background refetching. When data is being updated in the background, AsyncValue preserves the previous data so your UI doesn’t flicker, while isLoading (or isRefreshing in Riverpod 2) correctly indicates the network activity.
Common Operations
Section titled “Common Operations”Manual Refetching
Section titled “Manual Refetching”Use the provider’s notifier to trigger a manual refetch:
ElevatedButton( onPressed: () => ref.read(usersProvider.notifier).refetch(), child: Text('Refresh'),)Mutations
Section titled “Mutations”Mutations are triggered via the notifier’s mutate method:
final createUserProvider = mutationProvider<User, String>( (name) => api.createUser(name),);
// In your widget:final mutation = ref.watch(createUserProvider);
ElevatedButton( onPressed: mutation.isLoading ? null : () => ref.read(createUserProvider.notifier).mutate('John Doe'), child: Text('Create User'),)Next Steps
Section titled “Next Steps”queryProvider- Deep dive into standard queriesmutationProvider- Learn about server-side updatesinfiniteQueryProvider- Handle paginated data- Configuration - Global client and persistence setup
- Metadata Handling - Attach extra info for global side effects
- Circuit Breaker
- DevTools Integration
- Error Tracking
- Leak Detection
- Logging
- Metrics Exporters
- Security
- Prefetching - Warm up your cache