Riverpod — 一个可编译的状态和依赖管理器,用于Flutter,由Rémi Roussel于2021年创建,作为Provider的继承者。Riverpod解决了Provider的根本问题:缺乏编译时检查、对BuildContext的依赖以及ProviderNotFoundException的难题。根据pub.dev的数据,该包已收集超过5000个赞,并在新项目中积极取代Provider。
要点
Riverpod — 一个用于Flutter状态管理和依赖注入的库,它将provider的描述编译成安全的Dart代码。与Provider不同,Riverpod的provider不绑定到BuildContext:它们全局创建或在ProviderScope中创建,并且可以从任何地方访问。编译器在构建阶段检查provider图的类型、依赖关系和完整性,消除了像ProviderNotFoundException这样的运行时错误。
Riverpod使用override模型进行测试:每个provider都可以通过ProviderScope.overrideWith被覆盖,无需创建子类或模拟接口。这使得测试隔离:每个测试都获得自己完全可控的依赖图副本。
根据Flutter Community Survey 2025,Riverpod在受欢迎程度上排名第三,仅次于Provider和BLoC。同时,Riverpod是增长最快的包:2024年安装量增长+120%。主要原因:编译安全性、没有ProviderNotFoundException、通过AsyncValue内置的异步支持。
Riverpod提供8种provider类型,每种适用于特定场景:Provider(常量/服务)、StateProvider(基本状态)、StateNotifierProvider(带有StateNotifier的复杂逻辑)、ChangeNotifierProvider(用于从Provider迁移)、FutureProvider(异步数据,一次性)、StreamProvider(响应式流)、NotifierProvider(新API,Flutter 3.10+)和AsyncNotifierProvider(异步Notifier)。
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('$count');
}
}ProviderRef — 传递给每个provider以访问其他provider的对象。ref.watch — 订阅更改,ref.read — 一次性读取,ref.invalidate — 重置缓存。ProviderRef取代了Provider中的BuildContext:任何provider都可以在无需访问widget树的情况下读取其他provider。这使得可以在UI层之外构建依赖图。
ProviderScope — Riverpod运行所必需的根widget。ProviderScope存储所有provider,管理其生命周期并缓存值。没有ProviderScope,应用程序将因ProviderNotFoundException而崩溃。ProviderScope可以嵌套——嵌套的scope覆盖父级的provider,用于测试和功能隔离。
AsyncValue — Riverpod用于表示异步状态的sealed类。AsyncValue有三种变体:AsyncData(成功数据)、AsyncError(错误)、AsyncLoading(加载中)。无需手动在loading/error/data之间切换,每个FutureProvider或StreamProvider自动返回AsyncValue,widget通过ref.watch处理所有三种状态。
final userProvider = FutureProvider((ref) async {
final api = ref.watch(apiProvider);
return await api.fetchUser();
});
class UserScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userAsync = ref.watch(userProvider);
return userAsync.when(
data: (user) => UserWidget(user),
error: (e, _) => ErrorWidget(e.toString()),
loading: () => CircularProgressIndicator(),
);
}
}AsyncValue.when — 用于模式匹配所有三种状态的方法。编译器检查所有三种情况是否都已处理——如果忘记loading或error,代码将无法编译。AsyncValue.whenData — 仅用于data(如果不需要loading/error)。AsyncValue.guard — 用于将异常转换为AsyncError的try-catch包装器。keepAlive — 一个标志,防止在退出可见范围时销毁provider的缓存。
代码生成 — Riverpod 2.0+的关键特性。函数上的@riverpod注解自动生成具有正确类型、重构支持和自动补全的provider。代码生成使用riverpod_generator和build_runner。开发者编写纯函数,其余所有内容——类型、类、工厂构造函数——都自动生成。
@riverpod
String helloWorld(HelloWorldRef ref) {
return 'Hello World';
}
// 已生成:final helloWorldProvider = Provider((ref) => 'Hello World');
@riverpod
class Counter extends _$Counter {
int build() => 0;
void increment() => state++;
}Notifier — 用于带有代码生成的可变状态的新API。Notifier是一个带有build()方法和状态更改方法的类。与StateNotifier不同,Notifier不需要单独的状态类,并通过getter/setter提供对state的直接访问。Riverpod为每个带有@riverpod注解的Notifier类自动生成NotifierProvider。
build_runner:代码生成通过dart run build_runner build命令运行。生成的文件具有.g.dart后缀并导入到源代码中。当注解或provider类型发生变化时,需要重新运行代码生成。Riverpod 2.x推荐所有新项目使用代码生成——手动创建provider已过时。
主要区别在于Riverpod与Provider:独立于BuildContext、编译安全性、内置异步处理、自动缓存和通过override进行测试。Provider需要BuildContext来访问状态(context.watch,context.read),Riverpod使用WidgetRef和全局声明的provider。
| 特性 | Provider | Riverpod |
|---|---|---|
| 依赖BuildContext | 是 | 否 |
| 编译时检查 | 否 | 是(通过@riverpod) |
| ProviderNotFoundException | 运行时 | 不可能 |
| 异步 | 手动 | AsyncValue(内置) |
| 测试 | 在Provider中包装 | ProviderScope.overrideWith |
| 缓存 | 无 | 自动 + keepAlive |
从Provider迁移:Riverpod支持ChangeNotifierProvider.adaptive以使用现有的ChangeNotifier而无需重写。分阶段迁移:首先新功能使用Riverpod编写,然后旧的Provider通过适配器替换为Riverpod的provider。两个包可以在同一个项目中共存,允许在不冻结开发的情况下进行迁移。
测试Riverpod基于ProviderScope.overrideWith。每个provider在测试的ProviderScope内被覆盖,无需模拟和DI容器。ProviderContainer — 用于无Flutter(纯Dart)测试的隔离环境,允许在无需渲染widget的情况下测试provider。
import 'package:flutter_test/flutter_test.dart';
import 'package:riverpod/riverpod.dart';
void main() {
test('Counter increments correctly', () {
final container = ProviderContainer();
container.read(counterProvider.notifier).increment();
expect(container.read(counterProvider), 1);
});
testWidgets('UI updates on increment', (tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [counterProvider.overrideWithValue(5)],
child: CounterScreen(),
),
);
expect(find.text('5'), findsOneWidget);
});
}ProviderContainer — 无Flutter。使用ProviderContainer进行无需widget的provider单元测试。overrideWithValue — 用特定值替换provider。overrideWith — 用provider工厂替换(用于模拟服务)。autodispose — 在测试中检查provider在退出可见范围时是否通过container.dispose()被销毁。
常见问题
Riverpod — 具有全局provider、AsyncValue和代码生成的状态管理库。BLoC — 具有Event → Stream → State的架构模式。Riverpod更容易学习,并通过@riverpod注解提供更好的开发者体验。BLoC提供严格的业务逻辑隔离和通过BlocObserver进行的事件追踪。选择取决于项目的范式:Riverpod更接近Provider,BLoC更接近响应式流。
Autodispose — 当没有订阅时自动销毁provider的机制。默认情况下,所有Riverpod的provider都是autodispose:当widget退出树时,provider从内存中删除。keepAlive — 一个标志,用于禁用那些应该始终存活的provider(API客户端、存储库、设置)的autodispose。这可以防止内存泄漏——未使用的provider会自动销毁。
ref.invalidate — 强制重置provider缓存的方法。在invalidate之后,provider在下次读取时重新创建:FutureProvider重新执行async函数,StreamProvider重新订阅到流。使用invalidate强制刷新数据(下拉刷新、用户切换)。ref.refresh — invalidate + 读取的组合:在一个操作中重置并立即读取新值。
可以。Riverpod 1.x仅在没有代码生成的情况下工作——provider通过Provider()、StateNotifierProvider()、FutureProvider()等手动创建。Riverpod 2.x支持两种方法。没有代码生成会有更多的样板代码,但不需要依赖build_runner和dart run build_runner build。对于小型项目(最多30个provider),手动创建是合理的,对于大型项目,代码生成是必需的。
Family — 接受外部参数的provider修饰符。例如,userProvider(123) — 加载ID为123的用户的provider。Family provider为每个唯一参数分别缓存结果。对于每个元素按ID加载的元素列表,使用Family。Family修饰符适用于所有provider类型:Provider.family、FutureProvider.family、StreamProvider.family。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。