.family
Bevor sie das lesen, sollten sie sich über Providers und Wie man sie ausließt informieren.
In diesem Teil werden wir im Detail über den Provider-Modifikator .family
sprechen.
Der .family
-Modifikator hat einen Zweck: Einen Provider aus externen Werten zu erstellen.
Einige häufige Anwendungsfälle für family
wären:
- Kombination von FutureProvider mit
.family
, um eineMessage
anhand ihrer ID zu laden - Übergabe der aktuellen
Locale
an einen Provider, so dass wir mit Übersetzungen umgehen können:
Verwendung
Die family
's funktionieren, indem sie dem Provider einen zusätzlichen Parameter hinzufügen.
Dieser Parameter kann dann in unserem Provider frei verwendet werden, um einen Zustand zu erzeugen.
Zum Beispiel könnten wir family
mit FutureProvider kombinieren, um eine Message
anhand ihrer ID zu laden:
final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
return dio.get('http://my_api.dev/messages/$id');
});
Bei der Verwendung unseres messagesFamily
-Providers wird die Syntax dann leicht geändert.
Die übliche Syntax wird nicht mehr funktionieren
Widget build(BuildContext context, WidgetRef ref) {
// Error – messagesFamily is not a provider
final response = ref.watch(messagesFamily);
}
Stattdessen müssen wir einen Parameter an messagesFamily
übergeben:
Widget build(BuildContext context, WidgetRef ref) {
final response = ref.watch(messagesFamily('id'));
}
Es ist möglich, eine family
mit verschiedenen Parametern gleichzeitig zu verwenden.
Zum Beispiel könnten wir eine titleFamily
verwenden, um sowohl die französische als auch die englische
Übersetzungen gleichzeitig zu lesen:
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');
}
Parameter Einschränkungen
Damit family
's korrekt funktionieren, ist es wichtig, dass der an einen Provider
übergebene Parameter einen konsistenten hashCode
und ==
haben muss.
Idealerweise sollte der Parameter entweder ein primitiver Datentyp (bool/int/double/String),
eine Konstante (Provider) oder ein immutable Objekt, das ==
und hashCode
überschreibt, sein.
BEVORZUGE die Benutzung von autoDispose
wenn der Parameter keine Konstante ist:
Sie möchten vielleicht family
's verwenden, um die Eingabe eines Suchfeldes an Ihren Provider zu übergeben.
Dieser Wert kann sich jedoch häufig ändern und nie wieder verwendet werden.
Dies könnte zu Memory leaks führen, da ein Provider standardmäßig nie zerstört wird,
selbst wenn er nicht mehr verwendet wird.
Die Verwendung von .family
und .autoDispose
behebt dieses Speicherleck:
final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
return fetchCharacters(filter: filter);
});
Übergabe mehrerer Parameter an eine family
family
's haben keine eingebaute Unterstützung für die Übergabe mehrerer Werte an einen Provider.
Andererseits kann dieser Wert beliebig sein (solange er mit den den zuvor genannten Einschränkungen übereinstimmt).
Dazu gehört:
- Ein Tuple aus tuple
- Objekte mit Freezed oder built_value generiert
- Objekte die equatable benutzen
Hier ist ein Beispiel mit Freezed und 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);
// Benutze userId/locale
});
Widget build(BuildContext context, WidgetRef ref) {
int userId; // Lesen Sie die Benutzer-ID von irgendwoher
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);
// Benutze userId/locale
});
Widget build(BuildContext context, WidgetRef ref) {
int userId; // Lesen Sie die Benutzer-ID von irgendwoher
final locale = Localizations.localeOf(context);
final something = ref.watch(
exampleProvider(MyParameter(userId: userId, locale: locale)),
);
...
}