К содержимому

.family

До того, как приступить к этому разделу, прочтите о providers и как читать их. В этой части мы детально обсудим .family.

.family модификатор имеет единственное предназначение: создание уникального провайдера с помощью внешних параметров.

Несколько типичных случаев применения family:

  • Использование FutureProvider с .family для получения Message по ID
  • Передача текущей Locale в провайдер для правильного перевода

Использование

.family исполняет свое предназначение путем передачи дополнительного параметра в провайдер. Этот параметр можно использовать в провайдере для создания какого-либо состояния.

Например, мы можем использовать family с FutureProvider для получения Message по ID:

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

Для работы с нашим messagesFamily синтаксис немного отличается. Обычный синтаксис тут больше не работает.

Widget build(BuildContext context, WidgetRef ref) {
// Ошибка: messagesFamily не является провайдером
final response = ref.watch(messagesFamily);
}

Нам необходимо передать параметр в messagesFamily:

Widget build(BuildContext context, WidgetRef ref) {
final response = ref.watch(messagesFamily('id'));
}
к сведению

family можно использовать с разными аргументами одновременно. Например, мы можем воспользоваться titleFamily для получения французского и английского переводов в одно и то же время.


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');
}

Ограничения для параметров

Для исправной работы family важно указывать параметр с устойчивыми hashCode и ==.

В идеале параметр должен быть либо примитивным типом (bool/int/double/String), либо константой (Provider), либо неизменяемым объектом, в котором переопределен == и hashCode.

ПРЕДПОЧТИТЕЛЬНО использовать autoDispose, когда параметр не является константой:

Вам может прийти в голову идея использовать family для передачи поискового запроса в провайдер. Но такое значение может изменяться очень часто и никогда не будет переиспользовано. Это может привести к утечке памяти, т.к. по умолчанию провайдер никогда не аннулируется, даже если он больше не используется.

Совместное использование .family и .autoDispose решает проблему с утечкой памяти:

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

Передача нескольких параметров в family

family не поддерживает передачу множества параметров в провайдер.

С другой стороны, значение может быть любым (пока оно удовлетворяет ранее упомянутым ограничениям).

Пример таких значений:

Ниже представлен пример использования 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);
// Какие-то действия с userId/locale
});


Widget build(BuildContext context, WidgetRef ref) {
int userId; // Получение ID откуда-нибудь
final locale = Localizations.localeOf(context);

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

...
}