Flutter - 如何使用 Flutter Riverpod Generator 自动生成 Provider
Riverpod 是 Flutter 中一个强大的状态管理 & 数据绑定框架,它提供了很多种类的 Provider:
- 访问代码中的依赖项或配置(Provider)
- 缓存网络请求或其他场景下的异步数据(FutureProvider、StreamProvider)
- 管理本地状态(StateProvider、StateNotifierProvier)
但是手写众多的 Providers 很麻烦且容易出错,并且有时候很难选择使用哪个 Provider。
所以今后可以不再需要手写麻烦的代码。简单的使用 @riverpod
,让 build_runner
来帮助你快速生成 Providers。
这就是 riverpod_generator 包的用途所在。
手写 Provider
我们先来回顾下,如何手写 Provider。场景如下:
我们有一个类 ImageRepository 负责管理图片数据,类中有两个方法
getImageCount
和getImageSize
分别获取图片数量和图片尺寸。
传统的写法如下:
如果遇到更麻烦一点儿的 StateNotifierProvider
:
虽然静态分析能帮我们计算出类型,但代码可读性并不是很好。
@riverpod
入门
- 我们需要将必要的包,添加到
pubspec.yaml
里:
dependencies:
flutter_riverpod:
riverpod_annotation:
dev_dependencies:
build_runner:
riverpod_generator:
2. 获取包:
flutter pub get
3. 以 watch
的方式启动代码生成器:
flutter pub run build_runner watch -d
-d
参数是可选的,同 --delete-conflicting-outputs
。
这将监视我们项目中所有的 Dart 文件,并在我们进行更改时自动生成代码。
使用
使用分为4步:
- 导入 riverpod_annotation 包;
- 添加 part 文件;
- 使用 @riverpod;
- 更新内容。
一旦我们保存文件,
build_runner
就开始工作并在同一文件夹下生成 by_annotation_providers.g.dart
文件。
打开我们生成的文件,就能看到如下内容:
我们可以看到生成了
imageRepositoryProvider
以便于我们使用,还定义了类型为 AutoDisposeProviderRef<ImageRepository>
的 ImageRepositoryRef
。
复杂一点的情况同样很简单。相信我,你一旦稍微熟悉了使用,上手之后会觉得太轻松不过了。使用 @riverpod
将上面的改造为下面:
在 Widget 中可以这样使用:
最关键的是这行:
final imageSizeAsync = ref.watch(imageSizeProvider(imageId: imageId));
如我们所见,imageId
是命名参数,我们已经这样定义了它:
这意味着我们不再局限于定义一个只有一个参数的
FamilyProvider
。事实上,我们甚至不在乎我们是否使用 family
。我们所做的只是用一个 ref
对象和我们喜欢的任意数量参数来定义的函数,riverpod_generator
负责其他的工作。
生成的 familes 是怎么实现的?
我们来看一下 imageSizeProvider
是怎么实现的:
这里使用了callable classes,Dart 语言的一个简洁特性。它允许我们调用
imageSizeProvider(imageId: imageId)
而不是 imageSizeProvider.call(imageId: imageId)
。
支持 StreamProvider
如果我们有一个方法返回一个 Stream
,我们同样能生成对应的 Provider:
StateProvider 和 StateNotifierProvider
autoDispose 和 keepAlive
常见的需求就是,在不再使用时销毁 Provider 的状态。旧的语法是通过 autoDispose
来实现的。如果使用新的 @riverpod
,autoDispose
现在默认启用,并且重命名为 keepAlive
。
这意味着如下两段代码是等价的:
所以当我们把
keepAlive
设置为 true
,Provider 会保持活跃。
优缺点
优点
自动生成正确类型的 Provider
最大的优点是我们不需要弄清楚我们需要哪种 Provider,因为 code generator 会自动从函数签名中找到他们。新的 @riverpod
还可以声明带有一个或多个参数的复杂 Provider,另一个好处是生成的代码为每一个专有类型创建了一个 ref
对象,这可以很容易从函数名中推断出来:
imageRepository()
->imageRepositoryProvider
和ImageRepositoryRef
这使得运行时类型错误的可能性降低,因为如果我们不使用正确的类型,我们的代码将无法编译。
默认 autoDispose
使用新语法,所有生成的 Providers 都默认使用 autoDispose。 这是一个明智的选择,因为我们不应该保留不再使用的 Provider 的状态。
Provider 的热重载
当你在修改 provider 的代码时候,Riverpod 将会重新执行该 provider 并且只会执行该 provider。
缺点
代码生成
所有的缺点都归结为:Code Generation
即使最简单的 Provider 也会在单独的 .g.dart
文件中生成数十行代码,这会减慢构建过程并且会使项目因为额外的文件变得混乱。而且如果将 .g.dart
文件添加到版本控制中,将在每次更改的时候都会展示在 Pull Request 上。如果不想这样,我们可以把 .g.dart
文件添加到 。gitignore
文件中。
而且由于在开发过程中一直需要 flutter pub run build_runner watch
,如果是很大的项目使用 build_runner
,则需要更强大的电脑。
并不支持所有的 Provider
刚才也提到了,只支持以下:
Provider
FutureProvider
StreamProvider
NotifierProvider
AsyncNotifierProvider
虽然不支持 StateProvider
和 StateNotifierProvider
,但可以使用 Notifier
和 AsyncNotifier
来替换他们。
结论
正如我们所见,riverpod_generator 包提供了很多功能,以下是使用它的几个原因:
- 自动生成正确类型的 Provider
- 更容易创建带参数的 Provider,克服旧的
family
修饰符语法的局限性 - 提高了类型安全性并减少了运行时的类型错误
- 默认
autoDispose
但是,不支持某些旧的 Provider类型。
而且由于依赖于代码生成,因此您必须:
- 处理项目中额外的自动生成的文件
- 决定是否应该将生成的文件添加到 git
总的来说,最显著的优势是提高了开发人员的生产力。新语法意味着你又要重新学习和使用一个更小更熟悉的 API。这使得 Riverpod 更容易被旧 API 搞糊涂的开发者所接受。
要不要试试呢?😎
转载自:https://juejin.cn/post/7239249100335005757