随着每一个新版本的发布,Riverpod 及其生态系统都在不断完善:
- 核心软件包为反应式缓存和数据绑定提供了强大的应用程序接口
- Riverpod Generator 软件包简化了学习曲线并带来了显著的可用性改进(我在这里已经介绍过了)
- Riverpod Snippets扩展帮助我们轻松创建提供者和消费者
新的 Riverpod Lint 添加了许多有用的Lint和重构选项,让编写 Flutter 应用程序变得轻而易举。
等等...
还不止这些。
使用 Riverpod 进行现代开发是一件乐事,因为你只需编写少量代码,就能编写搜索和分页等复杂功能(让工具来指导你)。
在本文中,我将向你展示我是如何使用所有最新的好东西,包括代码生成、代码片段、重构选项和新的Riverpod lints,将我的时间跟踪应用程序重构为新的@riverpod语法的。
本文将展示 Riverpod Generator、Riverpod Lint 和 Riverpod Snippets extension 如何完美结合使用,但并不打算将其作为完整的资源。
添加所有必需的packages
由于我们将使用 Riverpod Generator 和 Riverpod Lint,因此需要在 pubspec.yaml 文件中添加一些packages:
dependencies:
# the main riverpod package for Flutter apps
flutter_riverpod:
# the annotation package containing @riverpod
riverpod_annotation:
dev_dependencies:
# a tool for running code generators
build_runner:
# the code generator
riverpod_generator:
# riverpod_lint makes it easier to work with Riverpod
riverpod_lint:
# import custom_lint too as riverpod_lint depends on it
custom_lint:
我们还需要在 analysis_options.yaml 中启用 custom_lint 插件:
analyzer:
plugins:
- custom_lint
接下来,我们需要在观察模式下启动 build_runner:
dart run build_runner watch -d
-d 标志是可选的,与 --delete-conflicting-outputs 相同。顾名思义,它可以确保我们覆盖先前构建中的任何冲突输出(这通常是我们想要的)。
这将监控项目中的所有 Dart 文件,并在我们进行修改时自动更新生成的代码。
现在设置已经完成,让我们来看一些代码。👇
使用Riverpod Generator 和 Riverpod Lint重构示例
假设我们有一个 AuthRepository 类,用来封装 FirebaseAuth 类:
// firebase_auth_repository.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AuthRepository {
AuthRepository(this._auth);
final FirebaseAuth _auth;
Stream<User?> authStateChanges() => _auth.authStateChanges();
}
假设我们也有这些providers:
// Make the [FirebaseAuth] instance accessible as a provider
final firebaseAuthProvider = Provider<FirebaseAuth>((ref) {
return FirebaseAuth.instance;
});
// Make the [AuthRepository] instance accessible as a provider
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return AuthRepository(ref.watch(firebaseAuthProvider));
});
// A [StreamProvider] for the [authStateChanges] stream
final authStateChangesProvider = StreamProvider.autoDispose<User?>((ref) {
return ref.watch(authRepositoryProvider).authStateChanges();
});
如何将providers转换为新的 Riverpod 生成器语法?
添加prat文件
正如我们在关于 Riverpod Generator 的文章中所看到的,第一步是添加一个.part文件。
通过使用 Flutter Riverpod Snippets 扩展,我们只需键入几个字符即可:
扩展会自动使用正确的文件名完成这一操作:
part 'firebase_auth_repository.g.dart';
转换一个简单的provider
接下来,让我们看看如何转换这个provider:
final firebaseAuthProvider = Provider<FirebaseAuth>((ref) {
return FirebaseAuth.instance;
});
同样,我们可以从输入 riverpod 开始,得到一个选项列表:
在决定选择哪个选项时,我们可以问自己
- 这个provider会返回一个对象、一个 Future、一个流还是一个 Notifier?
- 当不再被监听时,它应该自行处置,还是继续存活?
由于 FirebaseAuth 是一个在整个应用生命周期中都保持存活的单例,因此我们可以选择 riverpodKeepAlive 选项,最后得到如下结果:
下一步是在空白处添加
- 返回类型
- 函数名称
- 添加其它附加参数(本例中没有)
- provider主体功能
这就是我们的最终结果:
@Riverpod(keepAlive: true)
FirebaseAuth firebaseAuth(Ref ref) { // Stateless providers must receive a ref matching the provider name as their first positional parameter.dart(stateless_ref)
return FirebaseAuth.instance;
}
这段代码几乎是正确的。但 Riverpod Lint 提醒我们必须使用正确的类型,因为生成器会为每个provider创建特定的 Ref 类型。
事实上,函数名称(firebaseAuth)与生成的 Ref 类型和provider名称之间有严格的关系:
- firebaseAuth() → FirebaseAuthRef 和 firebaseAuthProvider
因此,让我们再次使用 “快速修复”:
然后就好了!Lint警告就消失了:
@Riverpod(keepAlive: true)
FirebaseAuth firebaseAuth(FirebaseAuthRef ref) {
return FirebaseAuth.instance;
}
只要 build_runner 仍在观察模式下运行,就会生成一个 firebaseAuthProvider(在 part 文件中),并可在我们的代码中使用。
重构其余provider
接下来,我们还需要重构其余两个provider:
// Make the [AuthRepository] instance accessible as a provider
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return AuthRepository(ref.watch(firebaseAuthProvider));
});
// A [StreamProvider] for the [authStateChanges] stream
final authStateChangesProvider = StreamProvider<User?>((ref) {
return ref.watch(authRepositoryProvider).authStateChanges();
});
在 Riverpod Snippets 和 Riverpod Lint 的帮助下,这很容易做到:
@Riverpod(keepAlive: true)
AuthRepository authRepository(AuthRepositoryRef ref) {
return AuthRepository(ref.watch(firebaseAuthProvider));
}
@riverpod
Stream<User?> authStateChanges(AuthStateChangesRef ref) {
return ref.watch(authRepositoryProvider).authStateChanges();
}
请注意,我选择在 firebaseAuthProvider 和 authRepositoryProvider 中使用 keepAlive,但没有在 authStateChangesProvider 中使用 keepAlive。这是有道理的,因为前两个provider包含长期依赖关系,而第三个provider可能需要也可能不需要始终监听。
示例 生成 AsyncNotifier
除了为objects, futures, 和 streams创建provider外,我们还想为 Notifier 和 AsyncNotifier 等类生成provider。
例如,下面是我的项目中的 AsyncNotifier 子类:
class EditJobScreenController extends AutoDisposeAsyncNotifier<void> {
@override
FutureOr<void> build() {
// omitted
}
// some methods
}
我本可以手工转换。
不过,Riverpod Snippets 提供了方便的 riverpodAsyncClass 和 riverpodClass 选项,再次帮助了我们:
选择上述选项后,我们就得到了这样的代码:
然后,我们就可以填补空白了:
@riverpod
class EditJobScreenController extends _$EditJobScreenController {
@override
FutureOr<void> build() {
// omitted
}
}
Riverpod Lint 还能做什么?
上面的示例展示了如何将现有provider或notifiers转换为新语法。
但你还可以用Riverpod Lint做更多事情,包括:
- 从StatelessWidget转换为ConsumerWidget或ConsumerStatefulWidget
- 在functional 和 class variants 之间转换
请再次查看官方文档,了解完整的选项列表。
总结
在 Riverpod 的早期,选择正确的provider并使用正确的语法很困难(尤其是在处理带有参数的复杂provider时)。
但正如我们所见,Riverpod Generator 和 Riverpod Lint 让我们的工作变得简单多了。
如今,将任何provider转换为新的 @riverpod 语法只需以下步骤:
- 使用 Riverpod Snippets 扩展添加part文件
- 选择正确的provider(同样使用 Riverpod Snippets)
- 填写空格(返回类型、函数名和参数)
- 选择正确的 Ref 类型(Riverpod Lint 简化了这项工作)
完成这些后,我们就可以保存文件,然后 build_runner 就会处理剩下的工作。
本文翻译自:codewithandrea.com/articles/fl…
欢迎大家关注我的公众号——【群英传】,专注于「Android」「Flutter」「Kotlin」 我的语雀知识库——www.yuque.com/xuyisheng