Skip to content

连接 Widget 和 Provider 的桥梁:Consumer ​

一句话总结

“Consumer” 是一种特殊的 Widget,它就像一座桥,把 Widget 世界和 Provider 世界连接了起来。


“Consumer” 是一种特殊的 Widget,它就像一座桥,把 Widget 世界和 Provider 世界连接了起来。

Consumer 和普通的 Widget 之间唯一的真正区别就是:Consumer 能拿到一个 Ref 对象。这使得它们能够读取 Provider 的状态并监听其变化。详情请参阅 Refs。

Consumer 有几种不同的“口味”,主要是为了满足不同的个人偏好。你会发现:

  • Consumer,一个“建造者”风格的 Widget(类似于 FutureBuilder)。它允许你的 Widget 与 Provider 互动,而无需继承 StatelessWidget 或 StatefulWidget 之外的任何东西。

    dart
    // 我们像往常一样继承 StatelessWidget
    class MyWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // 一个类似 FutureBuilder 的 widget
        return Consumer(
          // "builder" 回调给了我们一个 "ref" 参数
          builder: (context, ref, _) {
            // 我们可以用这个 "ref" 来监听 provider
            final value = ref.watch(myProvider);
            return Text(value.toString());
          },
        );
      }
    }
  • ConsumerWidget,StatelessWidget 的一个变体。你不再继承 StatelessWidget,而是继承 ConsumerWidget。它的行为完全相同,只是 build 方法多了一个 WidgetRef 参数。

    dart
    // 我们继承 ConsumerWidget 而不是 StatelessWidget
    class MyWidget extends ConsumerWidget {
      // "build" 方法多了一个参数
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        // 我们可以用这个 "ref" 来监听 provider
        final value = ref.watch(myProvider);
        return Text(value.toString());
      }
    }
  • ConsumerStatefulWidget,StatefulWidget 的一个变体。

    同样,你不再继承 StatefulWidget,而是继承 ConsumerStatefulWidget。并且,你不再继承 State,而是继承 ConsumerState。其独特之处在于 ConsumerState 有一个 ref 属性。

    dart
    // 我们继承 ConsumerStatefulWidget 而不是 StatefulWidget
    class MyWidget extends ConsumerStatefulWidget {
      @override
      ConsumerState<MyWidget> createState() => _MyWidgetState();
    }
    // 我们继承 ConsumerState 而不是 State
    class _MyWidgetState extends ConsumerState<MyWidget> {
      // 现在有了一个 "this.ref" 属性可用
      @override
      Widget build(BuildContext context) {
        // 我们可以用这个 "ref" 来监听 provider
        final value = ref.watch(myProvider);
        return Text(value.toString());
      }
    }

此外,你还可以在 hooks_riverpod 包中找到额外的 Consumer。它们将 Riverpod 的 Consumer 与 flutter_hooks 结合了起来。如果你对 hooks 不感兴趣,可以忽略它们。

我应该用哪一个? ​

选择哪种 Consumer 主要取决于个人喜好。你完全可以只用 Consumer 来搞定一切。它比其他选项稍微冗长一些,但如果你不喜欢 Riverpod “侵入” StatelessWidget 和 StatefulWidget 的方式,那么这点代价是值得的。

但如果你没什么特别的偏好,我们推荐使用 ConsumerWidget(或者当你需要一个 State 时,使用 ConsumerStatefulWidget)。

为什么我们不能像 provider 包那样用 context.watch? ​

在像 provider 这样的替代包中,你可以使用 context.watch 来监听 Provider。只要你有 BuildContext,这在任何 Widget 中都行得通。那为什么 Riverpod 不这么做呢?

原因在于,如果纯粹依赖 BuildContext 而不是 Ref,就无法可靠地实现 Riverpod 的自动销毁功能。虽然有一些小技巧可以实现一个在 BuildContext 下“大部分时间能用”的版本,但问题是存在大量微妙的边缘情况,可能会悄无声息地破坏自动销毁功能。

这会导致内存泄漏,但这还不是真正的问题所在。

自动销毁更重要的意义在于,停止执行那些不再需要的代码。如果自动销毁失败,那么那个 Provider 可能会在后台持续不断地执行网络请求。

为了追求极致的可靠性,Riverpod 宁愿牺牲一点点便利性。