Back to Riverpod

Mutations (experimental)

website/docs/concepts2/mutations.mdx

2.0.0-dev.95.0 KB
Original Source

import { Link } from "/src/components/Link"; import CodeBlock from "@theme/CodeBlock"; import { trimSnippet } from "/src/components/CodeSnippet"; import listener from 'raw-loader!./mutations/listening.dart'; import keyed from 'raw-loader!./mutations/keyed.dart'; import generic from 'raw-loader!./mutations/generic.dart'; import triggering from 'raw-loader!./mutations/triggering.dart'; import switching from 'raw-loader!./mutations/switching.dart'; import resetting from 'raw-loader!./mutations/resetting.dart';

:::caution Mutations are experimental, and the API may change in a breaking way without a major version bump. :::

Mutations, in Riverpod, are objects which enable the user interface to react to state changes. A common use-case is displaying a loading indicator while a form is being submitted

In short, mutations are to achieve effects such as this: !

Without mutations, you would have to store the progress of the form submission directly inside the state of a provider. This is not ideal as it pollutes the state of your provider with UI concerns ; and it involves a lot of boilerplate code to handle the loading state, error state, and success state.

Mutations are designed to handle these concerns in a more elegant way.

Defining a mutation

Mutations are instances of the Mutation object, stored in a final variable somewhere.

dart
// A mutation to track the "add todo" operation.
// The generic type is optional and can be specified to enable the UI to interact
// with the result of the mutation.
final addTodo = Mutation<Todo>();

:::note Typically, this variable will either be global or as a static final variable on a Notifier. :::

Listening to a mutation

Once we've defined a mutation, we can start using it inside <Link documentID="concepts2/consumers" /> or <Link documentID="concepts2/providers" />.
For this, we will need a <Link documentID="concepts2/refs" /> and pick a listening method of our choice (typically Ref.watch).

A typical example would be:

<CodeBlock>{trimSnippet(listener)}</CodeBlock>

Scoping a mutation

Sometimes, you may want to have multiple instances of the same mutation.

This can include things like an id, or any other parameter that makes the mutation unique.

This is useful if you want to have multiple instances of the same mutation, such as deleting a specific item in a list

Simply call the mutation with the unique key:

<CodeBlock>{trimSnippet(keyed)}</CodeBlock>

Sometimes, these mutations have a generic return type, such as if an api response may have different response types based on the input parameters, such as with deserialization.

<CodeBlock>{trimSnippet(generic)}</CodeBlock>

Triggering a mutation

So far, we've listened to the state of a mutation, but nothing actually happens yet.

To trigger a mutation, we can use Mutation.run, pass our mutation, and provide an asynchronous callback that updates whatever state we want. Lastly, we'll need to return a value matching the generic type of the mutation.

<CodeBlock>{trimSnippet(triggering)}</CodeBlock>

The different mutation states and their meaning

Mutations can be in one of the following states:

  • MutationPending: The mutation has started and is currently loading.
  • MutationError: The mutation has failed, and an error is available.
  • MutationSuccess: The mutation has succeeded, and the result is available.
  • MutationIdle: The mutation has not been called yet, or has been reset.

You can switch over the different states using a switch statement:

<CodeBlock>{trimSnippet(switching)}</CodeBlock>

After a mutation has been started once, how to reset it to its idle state?

Mutations naturally reset themselves to MutationIdle if:

  • They have completed (either successfully or with an error).
  • All listeners have been removed (e.g. the spinner widget has been removed)

This is similar to how <Link documentID="concepts2/auto_dispose"/> works, but for mutations.

Alternatively, you can manually reset a mutation to its idle state by calling the Mutation.reset method:

<CodeBlock>{trimSnippet(resetting)}</CodeBlock>