【问题标题】:Triggering Widget Rebuilds with Provider's context.read<T>() Method使用 Provider 的 context.read<T>() 方法触发小部件重建
【发布时间】:2021-09-21 04:59:52
【问题描述】:

根据 Flutter 的文档和 this example,据我了解,Provider 包的 context.read&lt;T&gt;context.watch&lt;T&gt; 方法之间的关键区别与触发小部件重建有关。您可以在任何小部件的 build 方法中调用 context.watch&lt;T&gt;() 以访问当前状态,并在状态发生变化时要求 Flutter 重建您的小部件。您不能在构建方法之外使用context.watch&lt;T&gt;(),因为这通常会导致细微的错误。相反,他们说,使用context.read&lt;T&gt;(),它会获取当前状态,但不会询问 Flutter 未来的重建。

我尝试制作这个简单的应用程序:

class MyDataNotifier extends ChangeNotifier {
  String _testString = 'test';

  // getter
  String get testString => _testString;

  // update
  void updateString(String aString) {
    _testString = aString;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => MyDataNotifier(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(context.read<MyDataNotifier>().testString),
        ),
        body: Container(
          child: Level1(),
        ),
      ),
    );
  }
}

class Level1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          onChanged: (val) {
            context.read<MyDataNotifier>().updateString(val);
          },
        ),
        Text(context.read<MyDataNotifier>().testString),
      ],
    );
  }
}

所有电话都是counter.read&lt;T&gt;()。应用程序的状态发生变化,但 UI 不会使用新值重新构建。我必须将其中一个调用更改为 counter.watch&lt;T&gt;() 才能重建状态。

另一方面,在DZone's simple example 中,UI 重新构建,并且所有调用都对 context.read()。

他们的代码和我的有什么不同?为什么我不能用 counter.read() 调用重建?

【问题讨论】:

    标签: flutter dart flutter-provider state-management


    【解决方案1】:

    TLDR:快速浏览后,DZone 文章看起来有错误。

    更长的答案

    context.watch&lt;Foo&gt;() 做了两件事:

    1. 从树中返回状态实例
    2. context 标记为依赖于Foo

    context.read&lt;Foo&gt;() 只做 1)。

    当你的 UI 依赖于 Foo 时,你应该使用 context.watch,因为这会适当地通知 Flutter 该依赖关系,并且它将被正确地重建。

    总的来说,它归结为以下经验法则:

    • build() 方法或任何其他返回Widget 的方法中使用context.watch
    • onPressed 处理程序(和其他相关函数)中使用context.read

    人们似乎不恰当地使用context.read 的主要原因是出于性能原因。一般来说,在性能方面更喜欢context.read 而不是context.watch 是一种反模式。相反,如果您想限制小部件重建的频率,您应该使用context.select。当您的值经常更改时,这非常有用。

    假设你有以下状态:

    class FooState extends ChangeNotifier {
      // imagine this us updated very often
      int millisecondsSinceLastTap;
    
      // updated less often
      bool someOtherProperty = false;
    }
    

    如果您有一个显示someOtherProperty 的小部件,context.watch 可能会导致许多不必要的重建。相反,您可以使用 context.select 仅依赖于已处理状态的部分:

    // read the property, rebuild only when someOtherProperty changes
    final property = context.select((FooState foo) => foo.someOtherProperty);  
    return Text('someOtherProperty: $property');
    

    即使值经常更新,如果提供给 select 的函数的输出没有改变,小部件也不会重建:

    // even though millisecondsSinceLastTap may be updating often,
    // this will only rebuild when millisecondsSinceLastTap > 1000 changes
    final value = context.select((FooState state) => state.millisecondsSinceLastTap > 1000);
    return Text('${value ? "more" : "less"} than 1 second...');
    

    【讨论】:

    • 不错的答案。谢谢!
    猜你喜欢
    • 2021-01-25
    • 1970-01-01
    • 2019-12-09
    • 2021-09-20
    • 2019-12-09
    • 2020-06-14
    • 2020-04-21
    • 2020-12-15
    • 1970-01-01
    相关资源
    最近更新 更多