.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 не поддерживает передачу множества параметров в провайдер.
С другой стороны, значение может быть любым (пока оно удовлетворяет ранее упомянутым ограничениям).
Пример таких значений:
- tuple из tuple
- Объекты, созданные с помощью Freezed или built_value
- Объекты, использующие equatable
Ниже представлен пример использования Freezed и 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);
  // Какие-то действия с 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)),
  );
  ...
}
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);
  // Какие-то действия с 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)),
  );
  ...
}