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.- If successful, set the mutation state to MutationSuccess.
- If an exception is thrown, set the mutation state to MutationError.
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:
- Mutation.reset, to manually reset a mutation to its initial state.
- ProviderObserver, to react to mutation state changes.
- MutationState, the current state of a mutation.
- Implemented types
-
- ProviderListenable<
MutationState< ResultT> >
- ProviderListenable<
- 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