Flutter Query

Flutter Query

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) mutationFn

Required

The function that performs the mutation. Receives the variables passed to mutate() and a MutationFunctionContext containing:

  • client - The QueryClient instance
  • meta - Additional metadata from the mutation options
  • mutationKey - 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,
)? onMutate

Called 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,
)? onSuccess

Called 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,
)? onError

Called 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,
)? onSettled

Called 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?>? mutationKey

An 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)? retry

Default: 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 null to stop retrying
  • Return a Duration to 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? gcDuration

Default: 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.

ValueDescription
GcDuration(minutes: 5)Garbage collected after 5 minutes (default)
GcDuration.zeroImmediately garbage collected when complete
GcDuration.infinityNever garbage collected

meta

Map<String, dynamic>? meta

Additional 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? queryClient

A 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 status

The overall status of the mutation.

ValueDescription
MutationStatus.idleMutation has not been triggered yet or was reset
MutationStatus.pendingMutation is currently executing
MutationStatus.successLast mutation completed successfully
MutationStatus.errorLast mutation resulted in an error

data

TData? data

The last successfully resolved data from the mutation.

error

TError? error

The error from the most recent failed mutation, if any.

variables

TVariables? variables

The variables passed to the most recent mutate() call.

submittedAt

DateTime? submittedAt

The timestamp when the mutation was submitted.

failureCount

int failureCount

The number of consecutive failures. Increments on each failure, resets to 0 when the mutation succeeds.

failureReason

TError? failureReason

The error from the most recent failure. Unlike error, this is set immediately on failure before retries complete.

isPaused

bool isPaused

Whether the mutation is currently paused.

mutate

Future<TData> Function(TVariables variables) mutate

Triggers 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() reset

Resets 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.

PropertyEquivalent
isIdlestatus == MutationStatus.idle
isPendingstatus == MutationStatus.pending
isSuccessstatus == MutationStatus.success
isErrorstatus == MutationStatus.error

On this page