原文链接:About code generation | Riverpod
pub:riverpod | Dart Package (flutter-io.cn)
译时版本: 2.4.9
之前翻译过 Riverpod 的官方文档,现在随着版本更新,官方文档又多了很多新内容,所以再补充翻译一下。
之前翻译过的内容,现在官方文档有中文了。
Flutter状态管理库Riverpod官方文档翻译汇总 - 掘金 (juejin.cn)
关于代码生成
代码生成的想法是使用工具为我们生成代码。在 Dart 中,带来的副作用就是需要"编译"应用的附加步骤。该问题在不久的将来可能会被解决,因为 Dart 开发组已经在致力于该问题的潜在解决方案。
在 Riverpod 的上下文中,代码生成多少会改变定义一个 "provider" 的语法。例如,代替以下代码:
dart
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
});
使用代码生成,会如下编写:
dart
@riverpod
Future<User> fetchUser(FetchUserRef ref, {required int userId}) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
}
使用 Riverpod 时,代码生成完全是可选项。使用 Riverpod 时不使用代码生成也是完全可以的。同时,Riverpod 也拥抱代码生成并建议使用它。
如何安装和使用 Riverpod 代码生成器的信息,参考开始页面。确认下文档的侧边栏代码生成是可用的。
应该使用代码生成吗?
在 Riverpod 中代码生成是可选的。有了这个意识,可能想知道应该不应该使用代码生成。
回答是:大概是应该使用
使用代码生成是使用 Riverpod 的建议方式。这是更有前瞻性的途径并且这会尽力挖掘出 Riverpod 的最大潜力。
同时,很多应用已经使用了 Freezed 或 json_serializable 之类代码生成的包了。
这些情况下,项目很可能已经配置好了代码生成,使用 Riverpod 也会比较简单。
现在,代码生成是可选项是因为 build_runner
很多情况下不被推荐。但是既然 Dart 中静态元编程 是可用的,build_runner
将不会再是问题。因为这一点,在 Riverpod 中代码生成语法就将会是唯一可用的语法。
如果使用 build_runner
会带来破坏性的问题,那需要考虑的就是不要使用代码生成。但是这样也要意识到会错失一些特性,并且将来也必然会迁移到代码生成。
尽管如此,到时候 Riverpod 肯也会提供迁移工具使转换尽可能平滑。
使用代码生成的好处是什么?
你可能想知道:"如果在 Riverpod 中代码生成是可选的,那为什么使用它呢?"
通常使用包:会让生活更容易。这包括但不限于:
-
更好的语法,更具可读性/灵活性,并且有更简单的学习曲线。
- 不需要担心 provider 的类型。编写业务逻辑,然后选择最适合的 provider 。
- 语法不再像是定义"难受的全局变量"。取而代之的是定义自定义的函数/类。
- 向 provider 传递参数现在是不受限的。相比于使用 .family 的受限和传递单个占位参数,现在可以传递任意参数。包括命名参数,可选参数甚至是默认值。
-
在 Rivderpod 中所编写代码的状态热重载
-
调试器可拾取通过生成的附加元数据进行更好的调试。
-
Riverpod 的一些特性只在代码生成中可用。
语法
定义一个 provider :
使用代码生成定义一个 provider 时,意识到以下几点会有帮助:
- Provider 可定义为一个注解的函数或注解的类它们几乎是相同的,但是基于类的 provider 有几个优点,它能包含公共方法可扩展外部对象以修改 provider 的状态(副作用)。函数式的 provider 是编写只有
build
方法的基于类的 provider 的语法糖,因此不能通过 UI 进行修改。 - 支持 Dart 的所有异步类型(Future,FutureOr 和Stream)
- 当函数标记为异步时,provider会自动处理错误/加载中状态并暴露为 AsyncValue 。
函数式(无法使用公开方法执行副作用)
基于类 (可以使用公开方法执行副作用)
Sync
函数式
dart
@riverpod
String example(ExampleRef ref) {
return 'foo';
}
基于类
dart
@riverpod
class Example extends _$Example {
@override
String build() {
return 'foo';
}
// 添加一些改变状态的方法
}
异步 Future
函数式
dart
@riverpod
Future<String> example(ExampleRef ref) async {
return Future.value('foo');
}
基于类
dart
@riverpod
class Example extends _$Example {
@override
Future<String> build() async {
return Future.value('foo');
}
// 添加一些改变状态的方法
}
异步 Stream 函数式
dart
@riverpod
Stream<String> example(ExampleRef ref) async* {
yield 'foo';
}
基于类
dart
@riverpod
class Example extends _$Example {
@override
Stream<String> build() async* {
yield 'foo';
}
// 添加一些改变状态的方法
}
启用/禁用 autoDispose (自动清除):
使用代码生成时,provider 默认是 autoDispose(自动清除)的。这意味着当它们没有监听器绑定它们时(ref.watch/ref.listen),它们会自动释放自己。
该默认设定很切合 Riverpod 哲学。不使用代码生成的变量时,默认 autoDispose 是关闭的,这是为了适应客户从 package:provider
进行迁移。
如果想禁用 autoDispose ,可以传递 keepAlive: true
给注解。
dart
// AutoDispose 的 provider (keepAlive 默认是 false)
@riverpod
String example1(Example1Ref ref) => 'foo';
// 不会 autoDispose 的 provider
@Riverpod(keepAlive: true)
String example2(Example2Ref ref) => 'foo';
传递参数给 provider(family):
使用代码生成时,就不再需要依赖 family
修饰符向 provider 传递参数了。取而代之的是,provider 的主函数可以接收任意个娄的参数,包括命名参数,可选参数和默认值。
要注意尽管这些参数应该带有一致的 == 。这意味着要么值会被缓存,要么参数应该覆写 == 。
函数式
dart
@riverpod
String example(ExampleRef ref, int param1, {String param2 = 'foo', }) {
return 'Hello $param1 & param2';
}
基于类
dart
@riverpod
class Example extends _$Example {
@override
String build(int param1, {String param2 = 'foo', }) {
return 'Hello $param1 & param2';
}
// 添加一些改变状态的方法
}
从非代码生成的变量迁移:
使用非代码生成的变量时,需要手动决定 provider 的类型。下面的内容是转换为代码生成变量时相应的选项:
Provider
转换前
dart
final exampleProvider = Provider.autoDispose<String>(
(ref) {
return 'foo';
},
);
转换后
dart
@riverpod
String example(ExampleRef ref) {
return 'foo';
}
NotifierProvider
转换前
dart
final exampleProvider = NotifierProvider.autoDispose<ExampleNotifier, String>(
ExampleNotifier.new,
);
class ExampleNotifier extends AutoDisposeNotifier<String> {
@override
String build() {
return 'foo';
}
// 添加一些改变状态的方法
}
转换后
dart
@riverpod
class Example extends _$Example {
@override
String build() {
return 'foo';
}
// 添加一些改变状态的方法
}
FutureProvider
转换前
dart
final exampleProvider =
FutureProvider.autoDispose<String>((ref) async {
return Future.value('foo');
});
转换后
dart
@riverpod
Future<String> example(ExampleRef ref) async {
return Future.value('foo');
}
StreamProvider
转换前
dart
final exampleProvider =
StreamProvider.autoDispose<String>((ref) async* {
yield 'foo';
});
转换后
dart
@riverpod
Stream<String> example(ExampleRef ref) async* {
yield 'foo';
}
AsyncNotifierProvider
转换前
dart
final exampleProvider =
AsyncNotifierProvider.autoDispose<ExampleNotifier, String>(
ExampleNotifier.new,
);
class ExampleNotifier extends AutoDisposeAsyncNotifier<String> {
@override
Future<String> build() async {
return Future.value('foo');
}
// 添加一些改变状态的方法
}
转换后
dart
@riverpod
class Example extends _$Example {
@override
Future<String> build() async {
return Future.value('foo');
}
// 添加一些改变状态的方法
}
StreamNotifierProvider
转换前
dart
final exampleProvider =
StreamNotifierProvider.autoDispose<ExampleNotifier, String>(() {
return ExampleNotifier();
});
class ExampleNotifier extends AutoDisposeStreamNotifier<String> {
@override
Stream<String> build() async* {
yield 'foo';
}
// 添加一些改变状态的方法
}
转换后
dart
@riverpod
class Example extends _$Example {
@override
Stream<String> build() async* {
yield 'foo';
}
// 添加一些改变状态的方法
}