首页 > 解决方案 > 为什么我不能在 build() 中使用 context.read,但我可以使用 Provider.of with listen: false?

问题描述

文档中说这些是相同的,context.read只是Provider.of<x>(context, listen: false). 如果我尝试context.read在构建方法中使用,控制台中也会出现错误,但它没有解释原因。

我还发现了这个话题:Provider.of(context, listen: false) 是否等同于 context.read()? 但它没有回答“为什么”。

标签: flutterdartflutter-provider

解决方案


  • context.read不允许在里面build使用,因为在那里使用非常危险,并且有更好的解决方案可用。

  • Provider.of允许build向后兼容。

总体而言,其文档中解释了为什么context.read不允许内部的原因:build

如果该值仅用于事件,则不要在 build 中调用 [read]:

Widget build(BuildContext context) {
  // counter is used only for the onPressed of RaisedButton
  final counter = context.read<Counter>();

  return RaisedButton(
    onPressed: () => counter.increment(),
  );
}

虽然此代码本身没有错误,但这是一种反模式。在重构小部件以用于其他事情后,它很容易在将来导致错误counter,但忘记将 [read] 更改为 [watch]。

考虑在事件处理程序中调用 [read]:

Widget build(BuildContext context) {
  return RaisedButton(
    onPressed: () {
      // as performant as the previous previous solution, but resilient to refactoring
      context.read<Counter>().increment(),
    },
  );
}

这与之前的反模式具有相同的效率,但没有脆弱的缺点。

不要使用 [read] 来创建具有永远不会改变的值的小部件

Widget build(BuildContext context) {
  // using read because we only use a value that never changes.
  final model = context.read<Model>();

  return Text('${model.valueThatNeverChanges}');
}

虽然如果其他内容发生更改则不重建小部件的想法很好,但这不应该通过 [read] 来完成。依靠 [read] 进行优化是非常脆弱的,并且依赖于实现细节。

考虑使用 [select] 过滤不需要的重建

Widget build(BuildContext context) {
  // Using select to listen only to the value that used
  final valueThatNeverChanges = context.select((Model model) => model.valueThatNeverChanges);

  return Text('$valueThatNeverChanges');
}

虽然比 [read] 更冗长,但使用 [select] 更安全。它不依赖于 上的实现细节Model,并且不可能出现我们的 UI 不刷新的错误。


推荐阅读