一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
Riverpod的官方文档有多国语言,但是没有汉语,所以个人简单翻译了一版。
官网文档:Riverpod
GitHub:GitHub - rrousselGit/river_pod
Pub:riverpod | Dart Package (flutter-io.cn)
译时版本:riverpod 1.0.3
.family
阅读该指南之前,确保首先阅读关于 Provider 的内容 和 如何读取它们。
在这部分,我们会详细说一下关于 provoide 的 .family
修饰符的内容。
.family
修饰符有一个目的:获取基于外部参数的唯一 provider 。
使用 family
的一些场景可能会是:
- 用
.family
绑定 FutureProvider ,用它的 ID 获取Message
- 传递当前的
Locale
给 provider,这样可以处理本地化(多国语言)。
用法
family 的运行机制是通过为 provider 添加额外的参数。然后该参数可以在 provider 中自由使用以创建一些状态。
例如,可以用 FutureProvider 绑定 family
,用它的 ID 获取 Message
:
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 不是 provider
final response = ref.watch(messagesFamily);
}
取而代之的是,需要传递参数给 messagesFamily
:
Widget build(BuildContext context, WidgetRef ref) {
final response = ref.watch(messagesFamily('id'));
}
信息
使用 family 可以同时带不同的参数。 例如,可以使用
titleFamily
同时读取法语和英语的翻译:
@override 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
和 ==
的 provider 很重要。
理想的情况是,参数应该是基本类型(bool/整数/浮点数/字符串)、常数(provider),或覆写了 ==
和 hashCode
的不可变对象。
推荐 当参数会发生变化时使用 autoDispose
你可能想使用 family 传递一个查找相关的输入给 provider。 但是那个值经常改变,而且不会再被使用。 这会造成内存泄露,因为默认情况下 provider 永远不会被释放,即使不再使用。
You may want to use families to pass the input of a search field to your provider. But that value can change often and never be reused.
This could cause memory leaks as, by default, a provider is never destroyed even if no longer used.
同时使用 .family
和 .autoDispose
可以修复这种内存泄露::
final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
return fetchCharacters(filter: filter);
});
传递多个参数给 family
family 没有内置给 provider 传递多个值的支持。
另一方面,这个值可以是 任意的 (只要它满足前面提到的限制)。
这包括:
- 源自 tuple 的元组
- 用 Freezed 或 built_value 生成的对象
- 使用 equatable 的对象
这里有一个使用 Freezed 或 equatable 传递多个参数的示例:
-
Freezed
@freezed 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); // 使用用户ID或 locale 做一些处理 }); @override Widget build(BuildContext context, WidgetRef ref) { int userId; // 从其他什么地方读取用户 ID final locale = Localizations.localeOf(context); final something = ref.watch( exampleProvider(MyParameter(userId: userId, locale: locale)), ); ... }
-
Equatable
class MyParameter extends Equatable {
MyParameter({
required this.userId,
required this.locale,
});
final int userId;
final Locale locale;
@override
List<Object> get props => [userId, locale];
}
final exampleProvider = Provider.family<Something, MyParameter>((ref, myParameter) {
print(myParameter.userId);
print(myParameter.locale);
// 使用用户ID或 locale 做一些处理
});
@override
Widget build(BuildContext context, WidgetRef ref) {
int userId; // 从其他什么地方读取用户 ID
final locale = Localizations.localeOf(context);
final something = ref.watch(
exampleProvider(MyParameter(userId: userId, locale: locale)),
);
...
}