随着每一次新版本的发布,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
            
          
评论区