跳到主要内容

Riverpod 中的 Ref:连接 Provider 的桥梁

管

管理员

flutter

2025年12月22日 642 次阅读
本页目录

1. 什么是 Ref?

在 Riverpod 中,Ref 是你与各个 Provider 进行交互的主要接口。 如果将 Flutter Widget 树中的 BuildContext 比作组件与框架交互的桥梁,那么 Ref 就是 Provider 之间、或 Widget 与 Provider 之间交互的桥梁。

Ref 的主要作用:

  • 读取(Reading):获取 Provider 当前的数据。
  • 监听(Watching):订阅 Provider 的变化,当数据改变时触发重建。
  • 副作用(Listening):监听 Provider 的变化以执行某些操作(如弹窗、导航),而不触发重建。
  • 生命周期管理:控制 Provider 的销毁与缓存(例如 ref.keepAlive 或 ref.invalidate)。

2. 如何获取 Ref

根据你所在的代码位置不同,获取 Ref 的方式也有所不同。

2.1 在 Provider 内部(使用 riverpod_generator)

当你使用 @riverpod 定义 Provider 时,Ref 会自动作为参数传递给你的构建函数,或者作为属性存在于类中。

  • 函数式 Provider (Functional Provider): Ref 对象(或其具体子类型,如 HelloWorldRef)作为第一个参数传入。
```dart
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'my_provider.g.dart';

@riverpod
String helloWorld(Ref ref) {
  // 在这里可以使用 ref.watch 或 ref.read
  return 'Hello world';
}
```
  • 类式 Provider (Notifier Provider): Ref 是 Notifier 类的内置属性,可以直接通过 ref 访问。
```dart
@riverpod
class Counter extends _$Counter {
  @override
  int build() {
    // 在 build 方法中可以使用 ref
    // 例如:ref.watch(anotherProvider);
    return 0;
  }

  void increment() {
    // 在方法中也可以使用 ref
    // 例如:ref.read(authProvider);
    state++;
  }
}
```

2.2 在 Widget 内部

在 Flutter UI 中,我们需要使用 WidgetRef。它与 Provider 内部的 Ref API 非常相似,但专门用于 Widget。通常通过继承 ConsumerWidget 来获取。

class Home extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // ref 参数由 ConsumerWidget 提供
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}

3. Ref 的核心用法:交互的三种模式

Ref 提供了三种主要方法来与 Provider 交互,理解它们的区别至关重要。

3.1 ref.watch —— 响应式监听(推荐)

用途:获取 Provider 的值,并监听其变化。 场景:通常用于 Provider 的 build 方法中,或 Widget 的 build 方法中。

当被监听的 Provider 更新时,ref.watch 会导致调用它的 Widget 或 Provider 重新构建。这是 Riverpod 构建响应式 UI 的核心。

示例:组合 Provider

@riverpod
String name(Ref ref) => 'Riverpod';

@riverpod
String greeting(Ref ref) {
  // 监听 nameProvider。
  // 一旦 nameProvider 的值发生变化,greetingProvider 也会自动重新计算。
  final name = ref.watch(nameProvider);
  return 'Hello $name';
}

3.2 ref.listen —— 副作用监听

用途:监听 Provider 的变化,执行自定义逻辑(如显示 SnackBar、对话框、页面跳转),但不触发重建。 场景:通常用于 Widget 的 build 方法中(作为设置监听器的位置)。

ref.listen 接受两个参数:

  1. 目标 Provider。
  2. 一个回调函数 (previous, next),分别代表旧值和新值。

示例:显示 SnackBar

class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 监听 counterProvider
    ref.listen(counterProvider, (previous, next) {
      if (next > 5) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Count is high!')),
        );
      }
    });

    return Scaffold(/* ... */);
  }
}

3.3 ref.read —— 一次性读取(谨慎使用)

用途:获取 Provider 的当前值,不监听变化。 场景:仅用于由用户交互触发的回调函数中(如 onPressed、onTap)。

重要原则:

  • ❌ 绝对不要在 build 方法中使用 ref.read。这会破坏响应式机制。
  • ✅ 推荐在按钮点击事件等回调中使用。

示例:在按钮点击中读取

class MyButton extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return ElevatedButton(
      onPressed: () {
        // ✅ 正确用法:在回调中使用 ref.read
        // 我们不需要监听变化,只需要在点击的一瞬间获取 Counter 的类实例来调用方法
        ref.read(counterProvider.notifier).increment();
      },
      child: Text('Increment'),
    );
  }
}

4. 决策指南:我该用哪一个?

为了简化决策过程,请遵循以下决策流:

场景 推荐方法 理由
**我想

Related

相关文章

同分类文章 →
2026年10月31日 flutter

`auth()->id()` 到底返回谁?Sanctum 多态令牌与双会员系统的踩坑记录

## 引子先看一段看起来很正常的代码:```php$userId = auth('sanctum')->id();if ($userId) {    $res['isLiked'] = $this->likes()        ->where('user_id', $userId

2026年07月28日 flutter

Dart 3 记录(Records)和密封类(Sealed classes)

Dart 3 引入了几个极其强大的语言特性,其中最耀眼、对我们日常开发改变最大的就是 记录(Records) 和 密封类(Sealed classes),它们配合 模式匹配(Pattern Matching),让 Dart 的表现力达到了现代编程语言(如 Swift、Kotlin、Rust

2026年06月29日 flutter

flutter的 Clean Architecture架构设计

Clean Architecture(清晰架构)是一种将应用程序分层的设计模式,强调关注点分离与依赖规则,通过将业务逻辑与框架、UI、数据源解耦,提升代码的可测试性、可维护性与可扩展性。在 Flutter/Dart 环境下,Clean Architecture 通

WeChat

扫码加我微信

微信二维码