Mutation<ResultT> class sealed

Mutations are a form of UI state that represents "side-effects" (such as "submitting a form" or "saving a file", etc).

Mutations are a mean to enable the UI to both:

  • Preserve the state of providers while the operation is running.
  • Allow the UI to react to the state of the operation.

In particular, mutations are useful to show a loading indicator for a form submission, or a snackbar when the form submission has completed (either successfully or with an error).

Usage

Mutations are used like providers. They are defined as a top-level global final variable:

final addTodoMutation = Mutation<void>();

The generic type corresponds to the state of the mutation. This corresponds to data that the UI will be able to react to, to display special UI elements on success.

Once defined, you can use mutations using Ref.watch to listen to their state:

Widget build(BuildContext context, WidgetRef ref) {
  final addTodo = ref.watch(addTodoMutation);

  switch (addTodo) {
    case MutationIdle():
      return ElevatedButton(
        onPressed: () {
          /* Add a todo. See further down */
        },
        child: const Text('Add Todo'),
      );
    case MutationPending():
      return const CircularProgressIndicator();
    case MutationError():
      return Text('An error occurred: ${addTodo.error}');
    case MutationSuccess():
      return const Text('Todo added successfully!');
  }
}

This is for the UI logic. But on its own, the state of the mutation will never change.

To change the mutation state, you need to call Mutation.run. This is typically done inside a callback, such as the onPressed of a button:

ElevatedButton(
  onPressed: () {
    addTodoMutation.run(ref, (ref) async {
      // This is where you perform the side-effect. Here, you can
      // read your providers to modify them.
      await ref.read(todoListProvider.notifier).addTodo(
        Todo(title: 'New Todo'),
      );
    });
  },
);

Tapping the button will:

  • Set the mutation state to MutationPending.
  • Wait for todoListProvider.notifier.addTodo to complete.

Resetting to MutationIdle

By default, mutations are automatically reset to MutationIdle when they are no longer being listened to. This is similar to Riverpod's "auto-dispose" feature, for mutations.

If you do not wish for a mutation to be automatically reset, you can listen to its state in a provider/consumer using Ref.listen.

If your mutation is always listened, you may call Mutation.reset manually to restore the mutation to its MutationIdle state.

Concurrency

Currently, mutations do not restrict concurrent calls in any capacity.

This means that if you call Mutation.run while a previous call is still pending, the mutation state will be set to MutationPending again, and the previous call's return value will be ignored.

Passing keys to mutations to obtain a unique state

By default, the state of a mutation is shared across all places that listen to it.

But you may want to distinguish between different calls to the same mutation, based on some unique parameter. For example, given a deleteTodoMutation, you may want to distinguish between two different todos being deleted.

To do so, mutations optionally can take a key, by using call:

final deleteTodoMutation = Mutation<void>();
...

// You can pass a unique object to the mutation upon watching it.
final deleteTodo = ref.watch(deleteMutation(todo.id));
...

onPressed: () {
  // Upon calling `run`, you will have to pass the same key as when
  // watching the mutation.
  deleteTodo(todo.id).run(ref, (ref) async { /* ... */ });
}

See also:

Implemented types
Annotations
  • @immutable
  • @publicInMutations

Constructors

Mutation.new({Object? label})
factory

Properties

hashCode int
The hash code for this object.
no setterinherited
key Object?
A key to differentiate calls to the same mutation.
no setter
label Object?
The label of the mutation, used for debugging purposes.
no setter
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

call(Object? key) Mutation<ResultT>
Passes a key to the mutation, which will be used to distinguish between different calls to the same mutation.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
reset(MutationTarget container) → void
Resets the mutation to its initial state (MutationIdle).
run(MutationTarget target, Future<ResultT> cb(MutationRef ref)) Future<ResultT>
Starts a mutation and set its state based on the result of the callback.
toString() String
A string representation of this object.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited