为什么我不能在 build() 中使用 context.read,但我可以将 Provider.of 与 listen: false 一起使用?

Ser*_*sky 28 provider dart flutter

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

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

Rém*_*let 35

  • 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(),
  );
}
Run Code Online (Sandbox Code Playgroud)

虽然这段代码本身并没有被窃听,但这是一种反模式。将widget重构counter为其他东西后很容易导致日后的bug ,但是忘记把[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(),
    },
  );
}
Run Code Online (Sandbox Code Playgroud)

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

不要使用 [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}');
}
Run Code Online (Sandbox Code Playgroud)

虽然如果其他事情发生变化而不重建小部件的想法是好的,但这不应该用 [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');
}
Run Code Online (Sandbox Code Playgroud)

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


小智 5

问题是您尝试在小部件完成构建之前调用上下文,以在小部件完成构建后运行代码,将代码提供给后帧回调函数。

例如:

WidgetsBinding.instance.addPostFrameCallback((_) {
    // your code in here
});
Run Code Online (Sandbox Code Playgroud)