Skip to main content

Mutations

A mutation should be used to update data on the server. By its self a mutation doesn't really do anything. However, it comes with a few useful options for updating previously fetched queries.

Creating a mutation

A mutation is a small wrapper around a queryFn which is an asynchronous that will update the server. It takes two generic arguments, the first is the queryFn return type and the second is the argument type that will be passed to the queryFn.

final createPostMutation = Mutation<ReturnType, ArgType>(
queryFn: (post) => createPost(post),
);

To activate a mutation call the mutate function on the Mutation object.

final post = PostModel(
title: "Post title",
);
await createPostMutation.mutate(post);

The mutate function takes a single argument which must be the ArgType specified when instantiating the mutation. This argument will be passed to the queryFn. The mutate function returns a future that completes then the queryFn completes, so this can be awaited if you need.

Invalidating Queries

It is often a good idea to invalidate all queries that the mutation will affect. To easily do this pass a list of query keys when instantiating the mutation. If the mutation is successful all the queries matching the keys will be invalidated. Invalidating a query will not guarantee an immediate re-fetch, it will just mark the query as stale so that next time the query is requested it will be re-fetched. If you know that a query should be re-fetched immediately then use the refetchQueries prop instead.

final createPostMutation = Mutation<PostModel, PostModel>(
invalidateQueries: ['posts'],
queryFn: (post) => createPost(post),
);

Re-fetching Queries

To re-fetch a set of queries after a mutation completes pass a list of keys to the refetchQueries prop. This will loop through the keys are force a re-fetch on each query.

final createPostMutation = Mutation<PostModel, PostModel>(
refetchQueries: ['posts'],
queryFn: (post) => createPost(post),
);

Mutation Key and Cache

Unlike a query the mutation key is optional. A mutation will not be cached unless it is given a key. Caching a mutation is useful if the current state of the mutation is needed in multiple places. For example, you could have a loading spinner in the app bar while the mutation is called in a form submit button. In this case you would use a key to refer to the same mutation.

final createPostMutation = Mutation<PostModel, PostModel>(
key: "post mutation",
queryFn: (post) => createPost(post),
);

Mutation stream

Just like a query a mutation has a stream which emits the MutationState whenever it changes. There is no cacheDuration on a mutation so whenever the last listener is removed from a mutation it is immediately removed from memory.

Mutation Lifecycle

There are three lifecycle call backs for a mutation.

  • onStartMutation is called before the queryFn. This can be used for optimistic updates.
  • onSuccess is called after the queryFn if the mutation is successful.
  • onError is called after the queryFn if the mutation fails.

Error handling

The onError callback is called if the queryFn throws an error. Sometimes you may need to handle the error where you call the mutate function. To do this you can await the mutate function and use the mutation state to view the error.

final mutation = updateUser();
await mutation.mutate(user);
if(mutation.state.status == QueryStatus.error){
// handle error
}