useMutation
Hook for performing create, update, and delete operations
final MutationResult<TData, TError, TVariables, TOnMutateResult> result =
useMutation<TData, TError, TVariables, TOnMutateResult>(
mutationFn,
onMutate: onMutate,
onSuccess: onSuccess,
onError: onError,
onSettled: onSettled,
mutationKey: mutationKey,
retry: retry,
gcDuration: gcDuration,
meta: meta,
queryClient: queryClient,
);Parameters
mutationFn
Future<TData> Function(TVariables variables, MutationFunctionContext context) mutationFnRequired
The function that performs the mutation. Receives the variables passed to
mutate() and a MutationFunctionContext containing:
client- TheQueryClientinstancemeta- Additional metadata from the mutation optionsmutationKey- The mutation key, if provided
Must return a Future that resolves with data or throws an error.
useMutation<User, ApiError, CreateUserInput, void>(
(input, context) => api.createUser(input),
);onMutate
FutureOr<TOnMutateResult?> Function(
TVariables variables,
MutationFunctionContext context,
)? onMutateCalled before the mutation function is fired. Use this callback to perform
optimistic updates. The returned value is passed to onSuccess, onError, and
onSettled callbacks as onMutateResult.
useMutation<Todo, ApiError, CreateTodoInput, List<Todo>>(
(input, context) => api.createTodo(input),
onMutate: (input, context) {
// Snapshot the previous value
final previousTodos = context.client.getQueryData<List<Todo>>(['todos']);
// Optimistically update the cache
context.client.setQueryData<List<Todo>>(
['todos'],
(old) => [...?old, Todo(id: 'temp', title: input.title)],
);
// Return the snapshot for rollback
return previousTodos;
},
);onSuccess
FutureOr<void> Function(
TData data,
TVariables variables,
TOnMutateResult? onMutateResult,
MutationFunctionContext context,
)? onSuccessCalled when the mutation is successful. Receives the mutation result, the
variables, the value returned from onMutate, and the mutation context.
useMutation<User, ApiError, CreateUserInput, void>(
(input, context) => api.createUser(input),
onSuccess: (user, input, _, context) {
// Invalidate and refetch related queries
context.client.invalidateQueries(queryKey: ['users']);
},
);onError
FutureOr<void> Function(
TError error,
TVariables variables,
TOnMutateResult? onMutateResult,
MutationFunctionContext context,
)? onErrorCalled when the mutation encounters an error. Use this to handle errors or roll back optimistic updates.
useMutation<Todo, ApiError, CreateTodoInput, List<Todo>>(
(input, context) => api.createTodo(input),
onMutate: (input, context) {
final previousTodos = context.client.getQueryData<List<Todo>>(['todos']);
context.client.setQueryData<List<Todo>>(
['todos'],
(old) => [...?old, Todo(id: 'temp', title: input.title)],
);
return previousTodos;
},
onError: (error, input, previousTodos, context) {
// Roll back the optimistic update
if (previousTodos != null) {
context.client.setQueryData<List<Todo>>(['todos'], (_) => previousTodos);
}
},
);onSettled
FutureOr<void> Function(
TData? data,
TError? error,
TVariables variables,
TOnMutateResult? onMutateResult,
MutationFunctionContext context,
)? onSettledCalled when the mutation is either successful or errors. Always fires after
onSuccess or onError. Useful for cleanup operations that should run
regardless of outcome.
useMutation<Todo, ApiError, CreateTodoInput, void>(
(input, context) => api.createTodo(input),
onSettled: (data, error, input, _, context) {
// Always refetch to ensure consistency
context.client.invalidateQueries(queryKey: ['todos']);
},
);mutationKey
List<Object?>? mutationKeyAn optional key to identify this mutation. Unlike query keys, mutation keys are not required since mutations are not cached in the same way as queries.
Mutation keys can be used for:
- Debugging and observability
- Filtering mutations in the mutation cache
- Sharing mutation state between components
retry
Duration? Function(int retryCount, TError error)? retryDefault: No retries (unlike queries which default to 3)
A callback that determines whether to retry a failed mutation and how long to
wait. The retryCount starts at 0 after the first failure and increments with
each retry attempt.
- Return
nullto stop retrying - Return a
Durationto retry after that delay
// Never retry
retry: (_, _) => null,
// Retry 3 times with fixed 3 seconds delay
retry: (retryCount, error) {
if (retryCount >= 3) return null;
return const Duration(seconds: 3);
}
// Retry only on network errors
retry: (retryCount, error) {
if (error is NetworkException) {
return Duration(seconds: 1);
}
return null;
}gcDuration
GcDuration? gcDurationDefault: GcDuration(minutes: 5)
How long the mutation result remains in memory before garbage collection. After a mutation completes, its result will be garbage collected after this duration.
| Value | Description |
|---|---|
GcDuration(minutes: 5) | Garbage collected after 5 minutes (default) |
GcDuration.zero | Immediately garbage collected when complete |
GcDuration.infinity | Never garbage collected |
meta
Map<String, dynamic>? metaAdditional metadata stored on the mutation. Accessible in the
MutationFunctionContext passed to mutationFn and all callbacks.
useMutation<User, ApiError, CreateUserInput, void>(
(input, context) {
final source = context.meta?['source'];
return api.createUser(input, source: source);
},
meta: {'source': 'signup_form'},
);queryClient
QueryClient? queryClientA custom QueryClient to use. If not provided, uses the client from the nearest
QueryClientProvider.
Returns
The hook returns a MutationResult<TData, TError, TVariables, TOnMutateResult>
containing the status, data, error, and control methods.
status
MutationStatus statusThe overall status of the mutation.
| Value | Description |
|---|---|
MutationStatus.idle | Mutation has not been triggered yet or was reset |
MutationStatus.pending | Mutation is currently executing |
MutationStatus.success | Last mutation completed successfully |
MutationStatus.error | Last mutation resulted in an error |
data
TData? dataThe last successfully resolved data from the mutation.
error
TError? errorThe error from the most recent failed mutation, if any.
variables
TVariables? variablesThe variables passed to the most recent mutate() call.
submittedAt
DateTime? submittedAtThe timestamp when the mutation was submitted.
failureCount
int failureCountThe number of consecutive failures. Increments on each failure, resets to 0 when the mutation succeeds.
failureReason
TError? failureReasonThe error from the most recent failure. Unlike error, this is set immediately
on failure before retries complete.
isPaused
bool isPausedWhether the mutation is currently paused.
mutate
Future<TData> Function(TVariables variables) mutateTriggers the mutation with the given variables. Returns a Future that resolves
with the mutation result or throws on error.
final mutation = useMutation<User, ApiError, CreateUserInput, void>(
(input, context) => api.createUser(input),
);
// In your UI
ElevatedButton(
onPressed: mutation.isPending
? null
: () => mutation.mutate(CreateUserInput(name: 'John')),
child: Text(mutation.isPending ? 'Creating...' : 'Create User'),
);You can also await the mutation result:
try {
final user = await mutation.mutate(CreateUserInput(name: 'John'));
print('Created user: ${user.id}');
} catch (error) {
print('Failed to create user: $error');
}reset
void Function() resetResets the mutation to its initial idle state. Clears data, error,
variables, and resets status to idle.
ElevatedButton(
onPressed: mutation.reset,
child: Text('Reset'),
);Derived getters
These properties derive from status for convenience.
| Property | Equivalent |
|---|---|
isIdle | status == MutationStatus.idle |
isPending | status == MutationStatus.pending |
isSuccess | status == MutationStatus.success |
isError | status == MutationStatus.error |