mutationProvider
The mutationProvider manages server-side operations like creating, updating, or deleting data. Unlike queries, mutations are triggered imperatively and their state is exposed for UI feedback.
Basic Usage
Section titled “Basic Usage”To use a mutation, you define the provider and then use the notifier to trigger the transition.
import 'package:flutter_riverpod/flutter_riverpod.dart';import 'package:fasq_riverpod/fasq_riverpod.dart';
// 1. Define the mutation provider <Data, Variables>final createUserProvider = mutationProvider<User, String>( (name) => api.createUser(name),);
class CreateUserScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // 2. Watch the state for UI feedback final mutation = ref.watch(createUserProvider);
return Column( children: [ ElevatedButton( onPressed: mutation.isLoading ? null : () { // 3. Trigger the mutation via the NOTIFIER ref.read(createUserProvider.notifier).mutate('John Doe'); }, child: mutation.isLoading ? CircularProgressIndicator() : Text('Create User'), ),
if (mutation.hasError) Text('Error: ${mutation.error}'),
if (mutation.isSuccess) Text('Created: ${mutation.data!.name}'), ], ); }}[!IMPORTANT] Always call
.mutate()on the notifier (ref.read(provider.notifier).mutate(...)) rather than attempting to call it on the state object.
MutationState Properties
Section titled “MutationState Properties”The mutationProvider returns a MutationState<T> object, which provides the following properties:
status: The currentMutationStatus(idle, loading, success, error).data: The result of the mutation if successful.error: The error object if the mutation failed.isLoading,isSuccess,isError,isIdle: Helper getters for status.hasData,hasError: Helper getters for presence of data/error.
Side Effects (Success/Error)
Section titled “Side Effects (Success/Error)”Handle side effects like navigation, showing snackbars, or invalidating queries using MutationOptions:
final deleteUserProvider = mutationProvider<void, String>( (userId) => api.deleteUser(userId), options: MutationOptions( onSuccess: (data, variables) { // Invalidate related queries to refresh data ref.read(fasqClientProvider).invalidateQuery('users'.toQueryKey());
ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('User deleted')), ); }, onError: (error, variables) { print('Failed to delete: $error'); }, ),);Optimistic Updates
Section titled “Optimistic Updates”Implement optimistic updates to make your UI feel instant:
final updatePostProvider = mutationProvider<Post, Post>( (post) => api.updatePost(post), options: MutationOptions( onMutate: (post) { // 1. Cancel outgoing refetches ref.read(fasqClientProvider).cancelQuery(QueryKeys.post(post.id));
// 2. Snapshot the current value final previousPost = ref.read(fasqClientProvider).getQueryData<Post>( QueryKeys.post(post.id) );
// 3. Optimistically update to the new value ref.read(fasqClientProvider).setQueryData(QueryKeys.post(post.id), post);
return previousPost; // Return for rollback }, onError: (error, post, previousPost) { // 4. Rollback on error if (previousPost != null) { ref.read(fasqClientProvider).setQueryData( QueryKeys.post(post.id), previousPost ); } }, onSettled: (data, error, post) { // 5. Always refetch after error or success to synchronize ref.read(fasqClientProvider).invalidateQuery(QueryKeys.post(post.id)); }, ),);Offline Queueing
Section titled “Offline Queueing”Fasq supports queueing mutations when the device is offline:
final createCommentProvider = mutationProvider<Comment, String>( (content) => api.addComment(content), options: MutationOptions( queueWhenOffline: true, // Mutation will execute when back online ),);Next Steps
Section titled “Next Steps”queryProvider- Standard data fetchinginfiniteQueryProvider- Paginated list management- Offline Support - Learn more about offline capabilities