随着每一次新版本的发布,Riverpod 及其周边生态系统都变得更加强大:
- 核心包为我们提供了强大的响应式缓存和数据绑定的API。
- Riverpod Generator 包简化了学习曲线,并带来了显著的可用性改进(我已经在这里介绍过)。
- Riverpod Snippets extension 帮助我们轻松创建Provider和消费者。
而新的 Riverpod Lint 包则添加了许多有用的代码检查规则 和重构选项,使得编写Flutter应用变得轻松。
但还不止于此...
使用Riverpod进行现代开发是一种乐趣,因为你可以用很少的代码来编写复杂的功能,比如搜索和分页(并让工具来引导你)。
在这篇文章中,我将展示如何将我的时间跟踪应用 重构为新的 @riverpod
语法,利用最新的工具,包括代码生成、代码片段、重构选项和新的Riverpod代码检查规则。
本文将展示当Riverpod Generator、Riverpod Lint 和Riverpod Snippets extension 结合使用时,它们是一个很棒的组合,但不是一个完整的资源。请查阅每个包的文档以获取更多用例。
添加所有必需的包
因为我们将使用Riverpod Generator和Riverpod Lint,所以需要在我们的 pubspec.yaml
文件中添加一些包:
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:
flutter pub 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();
}
假设我们还有以下供应商:
// 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();
});
如何将上述提供的内容转换为新的Riverpod Generator语法?
添加一个part文件
正如我们在我关于Riverpod Generator的文章中所看到的,第一步是添加一个part文件。
而通过使用Flutter Riverpod Snippets扩展,我们只需输入一些字符即可:
我们可以使用Riverpod Snippets扩展来声明部分文件,该扩展会自动完成正确的文件名:
part 'firebase_auth_repository.g.dart';
转换一个简单的Provider
接下来,让我们看看如何转换这个Provider:
final firebaseAuthProvider = Provider<FirebaseAuth>((ref) {
return FirebaseAuth.instance;
});
再次,我们可以开始通过键入 riverpod
来获取选项列表:
创建Provider时可供选择的选项清单。在决定选择哪个选项时,我们可以考虑以下几点:
- 此Provider是否会返回一个对象、一个
Future
、一个Stream
还是一个Notifier
? - 当不再被监听时,它是否应自行处置,或者应保持存活?
由于FirebaseAuth
是一个在整个应用程序生命周期内保持存活的单例,我们可以选择riverpodKeepAlive
选项,最终得到如下结果:
从riverpodKeepAlive
片段的输出中,下一步是填写以下内容:
- 返回类型
- 函数名称
- 任何额外的参数(在这种情况下没有)
- 提供程序的主体
最终的内容如下:
@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;
}
这段代码几乎正确。但是RiverpodLint提醒我们必须使用正确的类型,因为生成器为每个Provider创建了特定的Ref
类型。
实际上,函数的名称(firebaseAuth
)和生成的Ref
类型以及Provider名称之间存在严格的关系:
firebaseAuth()
→FirebaseAuthRef
和firebaseAuthProvider
所以让我们再次使用"QuickFix": 使用"快速修复"选项来选择正确的引用类型,然后,咦!语法检查警告就消失了:
@Riverpod(keepAlive: true)
FirebaseAuth firebaseAuth(FirebaseAuthRef ref) {
return FirebaseAuth.instance;
}
只要 build_runner
仍然在观察模式下运行,就会生成一个 firebaseAuthProvider
(在部分文件内),并准备好供我们在代码中使用。
重构剩余的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
中没有使用。这是有道理的,因为前两个Provider包含长期存在的依赖项,而第三个可能需要始终监听,也可能不需要。
示例:生成 AsyncNotifier
除了为对象、futures 和流创建Provider之外,我们还希望为类如 Notifier
和 AsyncNotifier
生成Provider。
例如,以下是我在项目中使用的一个 AsyncNotifier
子类:
class EditJobScreenController extends AutoDisposeAsyncNotifier<void> {
@override
FutureOr<void> build() {
// omitted
}
// some methods
}
我本可以手动转换这段内容。
但是Riverpod Snippets再次为我们提供了便利的选项,包括riverpodAsyncClass
和riverpodClass
。 !
通过选择上面的选项,我们最终得到以下代码片段,用于创建一个 Riverpod 类型的类:
dart
import 'package:riverpod/riverpod.dart';
class YourRiverpodClass extends StateNotifier {
YourRiverpodClass(YourData state) : super(state);
// Define your provider here
}
final yourRiverpodProvider = StateNotifierProvider((ref) {
// Initialize and configure your provider
return YourRiverpodClass(YourData());
});
riverpodAsyncClass
选项的结果摘要是,然后,我们只需填写空白处:
@riverpod
class EditJobScreenController extends _$EditJobScreenController {
@override
FutureOr<void> build() {
// omitted
}
}
Riverpod Lint还能做什么?
上面的示例展示了如何将现有的Provider或通知器转换为新的语法形式。
但是,Riverpod Lint 还可以做很多其他事情,包括:
- 从
StatelessWidget
转换为ConsumerWidget
或ConsumerStatefulWidget
- 在函数式和类式变体之间进行转换
再次提醒,您可以查阅官方文档以获取完整选项列表。
结论
在Riverpod早期阶段,选择正确的Provider并正确使用语法(特别是处理带有参数的复杂Provider时)可能会比较困难。
但正如我们所见,Riverpod Generator 和 Riverpod Lint 让我们的生活变得更加轻松。
如今,将任何Provider转换为新的 @riverpod
语法只需要:
- 使用 Riverpod Snippets 扩展添加
part
指令 - 选择正确的Provider(再次使用 Riverpod Snippets)
- 填写必要信息(返回类型、函数名称和参数)
- 选择正确的
Ref
类型(Riverpod Lint 使这更容易)
完成上述步骤后,我们只需保存文件,然后 build_runner
会处理其余事项。
转自How to write Flutter apps faster with Riverpod Lint & Riverpod Snippets
评论区