.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 unMessage
à 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'));
}
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 :
- Le tuple de tuple.
- Les objets générés avec Freezed ou built_value.
- les objets utilisant equatable.
voici un example en utilisant Freezed et equatable:
- Freezed
- 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)),
);
...
}
class MyParameter extends Equatable {
MyParameter({
required this.userId,
required this.locale,
});
final int userId;
final Locale locale;
List<Object> get props => [userId, locale];
}
final exampleProvider = Provider.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)),
);
...
}