.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)),
);
...
}