可以将 Provider 理解为应用程序状态的“胶囊”。它负责包裹数据、处理逻辑,并提供缓存。
使用 Generator 时,你不再需要手动选择 FutureProvider 或 StateNotifierProvider,你只需要决定:是一个函数(只读/无副作用)还是一个类(可变状态)。
1. 基础:定义 Provider 的两种形态
在 riverpod_generator 中,所有的 Provider 默认都是 autoDispose(自动销毁)的。
A. 函数式 Provider (Functional)
适用场景: 只读数据、简单的计算、HTTP 请求(异步数据)。它替换了旧的 Provider, FutureProvider, StreamProvider。
语法特点: 直接写一个带有 @riverpod 注解的函数。
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'my_providers.g.dart'; // 必须引用生成文件
// 1. 同步数据 (替代 Provider)
@riverpod
String helloWorld(Ref ref) {
return 'Hello world';
}
// 2. 异步数据 (替代 FutureProvider)
// 返回 Future<T>,UI层会自动获得 AsyncValue<T>
@riverpod
Future<User> fetchUser(Ref ref) async {
final json = await http.get('api/user');
return User.fromJson(json);
}
B. 类 Provider (Class-based)
适用场景: 需要修改的状态、复杂的业务逻辑。它替换了旧的 StateNotifierProvider 和 NotifierProvider。
语法特点: 写一个类,继承生成的 _$类名,并必须实现 build 方法。
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter.g.dart';
// 替代 StateNotifierProvider / NotifierProvider
@riverpod
class Counter extends _$Counter {
// build 方法是初始化的入口,返回值就是状态的初始值
@override
int build() {
return 0;
}
// 定义修改状态的方法
void increment() {
state++; // 直接修改 state 即可触发 UI 更新
}
}
2. 进阶:传参 (Family)
在旧版本中,使用 .family 修改器传参非常繁琐且类型受限。 Generator 写法: 直接给函数或 build 方法加参数即可。
// 函数式:直接加参数
@riverpod
Future<User> fetchUserById(Ref ref, {required int id}) async {
return http.get('api/user/$id');
}
// 类式:在 build 方法中加参数
@riverpod
class UserProfile extends _$UserProfile {
@override
Future<User> build({required int userId}) async {
return http.get('api/user/$userId');
}
void updateName(String newName) {
// 逻辑...
}
}
调用时: ref.watch(fetchUserByIdProvider(id: 42))
3. 配置:生命周期 (KeepAlive)
默认情况下,Generator 生成的 Provider 在不再被监听时会自动销毁(AutoDispose)。如果你希望状态常驻内存(例如:包含用户 Token 的全局 User 对象),需要手动配置。
// keepAlive: true 意味着这个 Provider 不会自动销毁
@Riverpod(keepAlive: true)
String appTitle(Ref ref) {
return 'My App';
}
4. 消费:如何在 UI 中使用
无论怎么定义 Provider,在 Flutter Widget 中使用的方式是统一的。
| 方法 | 作用 | 适用场景 |
|---|---|---|
| ref.watch | 监听值变化 | 用在 build 方法中,用于重建 UI。 |
| ref.read | 读取一次值 | 用在回调函数中(如按钮点击),触发方法。 |
| ref.listen | 监听变化执行动作 | 用于导航、弹窗、显示 Snackbar。 |
示例代码:
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 1. 监听同步值
final hello = ref.watch(helloWorldProvider);
// 2. 监听异步值 (AsyncValue 处理加载/错误)
final userAsync = ref.watch(fetchUserProvider);
return Scaffold(
body: Column(
children: [
Text(hello),
// 优雅处理异步状态
userAsync.when(
data: (user) => Text(user.name),
loading: () => CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 3. 读取并调用方法 (注意:调用 Counter 类的方法)
ref.read(counterProvider.notifier).increment();
},
child: Icon(Icons.add),
),
);
}
}
总结:为什么要换成 Generator?
| 特性 | 旧版写法 (Legacy) | 新版写法 (Generator) |
|---|---|---|
| 语法 | Provider, FutureProvider, StateNotifierProvider... 容易混淆 |
@riverpod + 函数 或 类 (统一) |
| 传参 | .family (受限于单一参数,且类型推断差) |
直接在函数参数里写,支持命名参数,类型安全 |
| 销毁策略 | 默认常驻,需显式 .autoDispose |
默认自动销毁,更省内存 (Safe by default) |
| 代码量 | 模板代码较多 | 极简,把脏活累活交给代码生成器 |
通过使用 riverpod_generator,你只需要关注:我要返回什么数据? 以及 我是否需要修改它? 其余的 Provider 类型选择都由生成器自动完成。