.family
在阅读本文之前,请考虑阅读有关 providers 的内容以及 如何读取provider。
在本部分中,我们将详细讨论.family
修饰符。
.family
修饰符有一个目的:根据外部参数获取唯一的provider。
比如family
的一些常用的使用场景:
- 结合 FutureProvider 和
.family
通过它的ID来获取一条消息
。 - 将当前
区域
传递给provider,以便让我们可以处理翻译相关的内容。
用法
family的工作方式是向provider添加一个额外的参数。 然后可以在我们的provider中自由地使用这个参数来创建一些状态。
比如说,我们可以组合 family
和 FutureProvider 通过它的ID来获取一条 消息
:
final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
return dio.get('http://my_api.dev/messages/$id');
});
在使用 messagesFamily
provider时,语法略有不同。
通常的语法将不能使用:
Widget build(BuildContext context, WidgetRef ref) {
// Error – messagesFamily is not a provider
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正常工作,传递给provider的参数必须具有一致的 hashCode
和 ==
。
理想情况下,参数应该是基础类型(bool/int/double/String)、常量(providers)或重载 ==
和 hashCode
的不可变对象。
当参数不是常量时最好使用autoDispose
:
你可能希望使用family将搜索内容传递给你的provider。
但是这个值经常会改变,并且永远不会被重用。
这可能会导致内存泄漏,因为默认情况下,即使不再使用provider也不会销毁。
同时使用 .family
和 .autoDispose
可以解决内存泄漏的问题:
final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
return fetchCharacters(filter: filter);
});
将多个参数传递给family
family不支持将多个值传递给provider。
On the other hand, that value could be anything (as long as it matches with the restrictions mentioned previously). 另一方面,该值可以是任何东西(只要它在前面提到的限制当中)。
这包括:
- 一个元组 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);
// Do something with userId/locale
});
Widget build(BuildContext context, WidgetRef ref) {
int userId; // Read the user ID from somewhere
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);
// Do something with userId/locale
});
Widget build(BuildContext context, WidgetRef ref) {
int userId; // Read the user ID from somewhere
final locale = Localizations.localeOf(context);
final something = ref.watch(
exampleProvider(MyParameter(userId: userId, locale: locale)),
);
...
}