Back to Riverpod

Family

website/docs/concepts2/family.mdx

2.0.0-dev.93.3 KB
Original Source

import { Link } from "/src/components/Link"; import { AutoSnippet, } from "/src/components/CodeSnippet"; import functionalFamily from './family/functional'; import notifierFamily from './family/notifier'; import singleOverride from 'raw-loader!./family/single_override.dart'; import allOverride from 'raw-loader!./family/all_overrides.dart';

One of Riverpod's most powerful feature is called "Families".
In short, it allows a provider to be associated with multiple independent states, based on a unique parameter combination.

A typical use-case is to fetch data from a remote API, where the response depends on some parameters (such as a user ID or a search query or a page number). It enables defining a single provider that can be used to fetch and cache any possible parameter combination.

:::info If normal providers can be assimilated to a variable, then "family" providers can be assimilated to a Map. :::

Creating a Family

Defining a family is done by slightly modifying the provider definition to receive a parameter.

For functional providers, the syntax is as follows:

<AutoSnippet {...functionalFamily} />

And for notifier providers, the syntax is:

<AutoSnippet {...notifierFamily} />

:::info Although not strictly required, it is highly advised to enable <Link documentID="concepts2/auto_dispose" /> when using families.

This avoids memory leaks in case the parameter changes and the previous state is no longer needed. :::

Using a Family

Providers that receive parameters see their usage slightly modified too.

Long story short, you need to pass the parameters that your provider expects, as follows:

dart
final user = ref.watch(userProvider('123'));

:::caution Parameters passed need to have a consistent ==/hashCode.

View "family" as a Map, where the parameters are the key and the provider's state is the value. As such, if the ==/hashCode of a parameter changes, the value obtained will be different.

Therefore code such as the following is incorrect:

dart
// Incorrect parameter, as `[1, 2, 3] != [1, 2, 3]`
ref.watch(myProvider([1, 2, 3]));

To help spot this mistake, it is recommended to use the riverpod_lint and enable the provider_parameters lint rule. Then, the previous snippet would show a warning. See <Link documentID="introduction/getting_started" hash="enabling-riverpod_lintcustom_lint" /> for installation steps. :::

You can read as many "family" providers as you want, and they will all be independent. As such, it is legal to do:

dart
class Example extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final user1 = ref.watch(userProvider('123'));
    final user2 = ref.watch(userProvider('456'));

    // user1 and user2 are independent.
  }
}

Overriding families

When trying to mock a provider in tests, you may want to override a family provider.

In that scenario, you have two options:

  • Override only a specific parameter combination: <AutoSnippet raw={singleOverride} />
  • Override all parameter combinations: <AutoSnippet raw={allOverride} />