Riverpod — 本质、Flutter中的依赖编译

作者: IT Sectr 发布日期: 2026-02-19 阅读时间: 7 分钟

Riverpod — 一个可编译的状态和依赖管理器,用于Flutter,由Rémi Roussel于2021年创建,作为Provider的继承者。Riverpod解决了Provider的根本问题:缺乏编译时检查、对BuildContext的依赖以及ProviderNotFoundException的难题。根据pub.dev的数据,该包已收集超过5000个赞,并在新项目中积极取代Provider。

要点

  • ProviderRef — 用于在provider内部访问其他provider的对象
  • AsyncValue — 具有loading/error/data状态的异步数据包装器
  • Notifier — 具有更改方法的可变状态类
  • ProviderScope — 管理所有provider的根widget
  • Code Generation — 用于自动生成provider的@riverpod注解

什么是Riverpod?

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内置的异步支持。

Provider类型

Riverpod提供8种provider类型,每种适用于特定场景:Provider(常量/服务)、StateProvider(基本状态)、StateNotifierProvider(带有StateNotifier的复杂逻辑)、ChangeNotifierProvider(用于从Provider迁移)、FutureProvider(异步数据,一次性)、StreamProvider(响应式流)、NotifierProvider(新API,Flutter 3.10+)和AsyncNotifierProvider(异步Notifier)。

Dart
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与异步编程

AsyncValue — Riverpod用于表示异步状态的sealed类。AsyncValue有三种变体:AsyncData(成功数据)、AsyncError(错误)、AsyncLoading(加载中)。无需手动在loading/error/data之间切换,每个FutureProvider或StreamProvider自动返回AsyncValue,widget通过ref.watch处理所有三种状态。

Dart
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的缓存。

Code Generation和@riverpod

代码生成 — Riverpod 2.0+的关键特性。函数上的@riverpod注解自动生成具有正确类型、重构支持和自动补全的provider。代码生成使用riverpod_generatorbuild_runner。开发者编写纯函数,其余所有内容——类型、类、工厂构造函数——都自动生成。

Dart
@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 vs Provider

主要区别在于Riverpod与Provider:独立于BuildContext、编译安全性、内置异步处理、自动缓存和通过override进行测试。Provider需要BuildContext来访问状态(context.watch,context.read),Riverpod使用WidgetRef和全局声明的provider。

特性ProviderRiverpod
依赖BuildContext
编译时检查是(通过@riverpod)
ProviderNotFoundException运行时不可能
异步手动AsyncValue(内置)
测试在Provider中包装ProviderScope.overrideWith
缓存自动 + keepAlive

从Provider迁移:Riverpod支持ChangeNotifierProvider.adaptive以使用现有的ChangeNotifier而无需重写。分阶段迁移:首先新功能使用Riverpod编写,然后旧的Provider通过适配器替换为Riverpod的provider。两个包可以在同一个项目中共存,允许在不冻结开发的情况下进行迁移。

测试Riverpod

测试Riverpod基于ProviderScope.overrideWith。每个provider在测试的ProviderScope内被覆盖,无需模拟和DI容器。ProviderContainer — 用于无Flutter(纯Dart)测试的隔离环境,允许在无需渲染widget的情况下测试provider。

Dart
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与BLoC有何不同?

Riverpod — 具有全局provider、AsyncValue和代码生成的状态管理库。BLoC — 具有Event → Stream → State的架构模式。Riverpod更容易学习,并通过@riverpod注解提供更好的开发者体验。BLoC提供严格的业务逻辑隔离和通过BlocObserver进行的事件追踪。选择取决于项目的范式:Riverpod更接近Provider,BLoC更接近响应式流。

什么是Riverpod中的autodispose?

Autodispose — 当没有订阅时自动销毁provider的机制。默认情况下,所有Riverpod的provider都是autodispose:当widget退出树时,provider从内存中删除。keepAlive — 一个标志,用于禁用那些应该始终存活的provider(API客户端、存储库、设置)的autodispose。这可以防止内存泄漏——未使用的provider会自动销毁。

ref.invalidate如何工作?

ref.invalidate — 强制重置provider缓存的方法。在invalidate之后,provider在下次读取时重新创建:FutureProvider重新执行async函数,StreamProvider重新订阅到流。使用invalidate强制刷新数据(下拉刷新、用户切换)。ref.refresh — invalidate + 读取的组合:在一个操作中重置并立即读取新值。

可以在没有代码生成的情况下使用Riverpod吗?

可以。Riverpod 1.x仅在没有代码生成的情况下工作——provider通过Provider()、StateNotifierProvider()、FutureProvider()等手动创建。Riverpod 2.x支持两种方法。没有代码生成会有更多的样板代码,但不需要依赖build_runner和dart run build_runner build。对于小型项目(最多30个provider),手动创建是合理的,对于大型项目,代码生成是必需的。

什么是Family provider?

Family — 接受外部参数的provider修饰符。例如,userProvider(123) — 加载ID为123的用户的provider。Family provider为每个唯一参数分别缓存结果。对于每个元素按ID加载的元素列表,使用Family。Family修饰符适用于所有provider类型:Provider.family、FutureProvider.family、StreamProvider.family。

总结

  • Riverpod — 可编译的状态管理器,Provider的继承者,没有ProviderNotFoundException
  • ProviderRef — BuildContext的替代品,用于在其他provider内部访问provider
  • AsyncValue — 具有loading/error/data状态的sealed类,用于异步数据
  • @riverpod代码生成 — 自动推断provider类型和工厂
  • ProviderScope.overrideWith — 无需模拟和DI容器的隔离测试
  • Family — 具有独立缓存的参数化provider
  • autodispose和keepAlive — provider生命周期的自动管理

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读