Aller au contenu principal

.family

Avant de lire ceci, pensez à vous renseigner sur les providers et comment les lires.

Dans cette partie, nous allons parler en détail du modificateur de provider .family.

Le modifieur .family a un seul but : Créer un provider à partir de valeurs externes.

Certains cas d'utilisation communs pour family seraient :

  • Combinaison de FutureProvider avec .family pour récupérer un Message à partir de son ID.
  • Passer Locale à un provider, afin que nous puissions gérer les traductions (translations):

Usage

Le fonctionnement des familles consiste à ajouter un paramètre supplémentaire au provider. Ce paramètre peut ensuite être utilisé librement dans notre provider pour créer un état.

Par exemple, nous pourrions combiner family avec FutureProvider pour récupérer Message à partir de son ID :

final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
return dio.get('http://my_api.dev/messages/$id');
});

Ensuite, lorsque on utilise notre provider messagesFamily, la syntaxe est légèrement modifiée. La syntaxe habituelle ne fonctionnera plus :

Widget build(BuildContext context, WidgetRef ref) {
// Erreur – messagesFamily n'est pas un provider
final response = ref.watch(messagesFamily);
}

A la place, nous devons passer un paramètre à messagesFamily :

Widget build(BuildContext context, WidgetRef ref) {
final response = ref.watch(messagesFamily('id'));
}
info

Il est possible d'utiliser une famille avec différents paramètres simultanément. Par exemple, on peut utiliser un titleFamily pour lire à la fois les traductions française et anglaise en même temps :


Widget build(BuildContext context, WidgetRef ref) {
final frenchTitle = ref.watch(titleFamily(const Locale('fr')));
final englishTitle = ref.watch(titleFamily(const Locale('en')));

return Text('fr: $frenchTitle en: $englishTitle');
}

Restrictions sur les paramètres

Pour que les familles fonctionnent correctement, il est essentiel que le paramètre passé à un provider ait un hashCode et un == cohérents.

Idéalement, le paramètre devrait être soit une primitive (bool/int/double/String), une constante (providers), ou un objet immuable qui surcharge (overrid) == et hashCode.

PRÉFÉRER Utilisation de autoDispose lorsque le paramètre n'est pas constant :

Vous pouvez vouloir utiliser les familles pour transmettre la saisie d'un champ de recherche à votre provider. Mais cette valeur peut changer souvent et ne jamais être réutilisée. Cela pourrait provoquer des fuites de mémoire car, par défaut, un provider n'est jamais détruit même s'il n'est plus utilisé.

Utiliser à la fois .family et .autoDispose corrige cette fuite de mémoire :

final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
return fetchCharacters(filter: filter);
});

Passage de plusieurs paramètres à une famille

Les familles n'ont pas de support intégré (built-in) pour transmettre plusieurs valeurs à un provider.

D'autre par contre, cette valeur peut être n'importe quoi (pour autant qu'elle respecte les restrictions mentionnées précédemment).

Cela inclut :

voici un example en utilisant Freezed et equatable:


abstract class MyParameter with _$MyParameter {
factory MyParameter({
required int userId,
required Locale locale,
}) = _MyParameter;
}

final exampleProvider = Provider.autoDispose.family<Something, MyParameter>((ref, myParameter) {
print(myParameter.userId);
print(myParameter.locale);
// Faire quelque chose avec userId/locale.
});


Widget build(BuildContext context, WidgetRef ref) {
int userId; // Lire le user ID de quelque part
final locale = Localizations.localeOf(context);

final something = ref.watch(
exampleProvider(MyParameter(userId: userId, locale: locale)),
);

...
}