Skip to content

MutationBuilder

The MutationBuilder handles data updates (CRUD) in your app. Unlike queries, mutations are not cached but can trigger cache invalidations or updates.

import 'package:fasq/fasq.dart';
MutationBuilder<Todo, String>(
mutationFn: (text) => api.addTodo(text),
options: MutationOptions(
onSuccess: (newTodo) {
// Invalidate the 'todos' query so it refetches
QueryClient().invalidateQuery('todos'.toQueryKey());
},
),
builder: (context, state, mutate) {
return Column(
children: [
if (state.hasError) Text('Error: ${state.error}'),
ElevatedButton(
// Pass data to the mutation
onPressed: () => mutate('New Todo Item'),
child: state.isLoading
? const CircularProgressIndicator()
: const Text('Add Todo'),
),
],
);
},
)
NameTypeRequiredDescription
mutationFnFuture<T> Function(V)Yesfunction that performs the desired action.
builderWidget Function(BuildContext, MutationState<T>, MutateFunction<V>)YesBuilds the UI. Provides the state and a function to trigger the mutation.
optionsMutationOptions<T, V>?NoCallbacks for success, error, and optimistic updates.
PropertyTypeDescription
dataT?The result of the mutation.
errorObject?The error if the mutation failed.
statusMutationStatusidle, loading, success, or error.
isLoadingboolTrue while the mutation is running.

Update the UI before the server responds for a snappy experience.

MutationBuilder<Todo, Todo>(
mutationFn: (todo) => api.updateTodo(todo),
options: MutationOptions(
// Runs before the mutation function
onMutate: (newTodo, _) {
final client = QueryClient();
// 1. Cancel ongoing queries
client.cancelQuery('todos'.toQueryKey());
// 2. Snapshot previous value
final prevTodos = client.getQueryData('todos'.toQueryKey());
// 3. Optimistically update cache
client.setQueryData(
'todos'.toQueryKey(),
(old) => (old as List).map((t) => t.id == newTodo.id ? newTodo : t).toList()
);
// Return context to rollback if needed (optional)
return {'prevTodos': prevTodos};
},
onError: (err, newTodo, context) {
// Rollback on error
if (context != null) {
QueryClient().setQueryData('todos'.toQueryKey(), context['prevTodos']);
}
},
onSettled: (_, __, ___, ____) {
// Always refetch to ensure sync
QueryClient().invalidateQuery('todos'.toQueryKey());
}
),
builder: (context, state, mutate) {
// ...
}
)
MutationBuilder<User, Map<String, dynamic>>(
mutationFn: (data) => api.createUser(data),
options: MutationOptions(
onSuccess: (user) {
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Welcome ${user.name}!')),
);
},
),
builder: (context, state, mutate) {
return ElevatedButton(
onPressed: () {
if (formKey.currentState!.validate()) {
mutate({'name': nameController.text});
}
},
child: Text('Create Account'),
);
},
)