Flutter中Provider的一般用法(二)

在上篇文章中,已经对Provider的用法有了一定了解,下面在看下context.read() 和context.watch()的用法,为什么可以直接用context获取呢,BuildContext有两个拓展实现ReadContext, WatchContext:

extension ReadContext on BuildContext {
  T read() {
    return Provider.of(this, listen: false);
  }
}
extension WatchContext on BuildContext {
  T watch() {
    return Provider.of(this);
  }
}

ReadContext暴露了一个read方法,可以从最近的父Provider中获取类型为T的值,这个正好跟WatchContext.watch方法相反的。如果用read方法获取的Provider,那么当value改变的时候,不会使页面重建,而且这个方法不能再StatelessWidget.build和State.build方法中调用,也就是说可在这些方法外面随意调用,如果这种方式不符合自己的风格,完全可用Provider.of(conext, listen:false)的方式,毕竟read方式只是它的封装方式,虽然是做同样的事,但是没有上面说的限制。不要在build方法里调用read方法,如果只是用于时间处理的话,比如下面的代码:

Widget build(BuildContext context) {
  // counter is used only for the onPressed of RaisedButton
  final counter = context.read();
  return RaisedButton(
    onPressed: () => counter.increment(),
  );

虽然这么调用是本身是没bug的,这是个反例,它可能造成bug在未来重构widget的时候,在其他场景下使用counter,但是忘记把read方法改成watch获取provider,所以应该考虑在事件句柄里面调用read,就像下面这样:

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

上面这两种方式效果上是一样的,但是后面一种更加健壮。不要在构建widget时,使用read方式去获取一个从不改变的值,即使不会导致widget重构,虽然这个想法是好的,但也不该用read:

Widget build(BuildContext context) {
  // using read because we only use a value that never changes.
  final model = context.read();
  return Text('${model.valueThatNeverChanges}');
}

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');
}

虽然用select的方式更多代码,但是更安全,它不依赖Model的具体实现,不会造成UI不刷新的bug。使用read方法可以以一种更简单的方式去依赖其他对象,更随意的传递其他对象,而且可以不用依赖BuildContext:

class Model {
  Model(this.context);
  final BuildContext context;
  void method() {
    print(Provider.of(context));
  }
}
// ...
Provider(
  create: (context) => Model(context),
  child: ...,
)

相比上面的方式,我们更倾向于下面方式:

class Model {
  Model(this.read);
  // `Locator` is a typedef that matches the type of `read`
  final Locator read;
  void method() {
    print(read());
  }
}
// ...
Provider(
  create: (context) => Model(context.read),
  child: ...,
)

上面两种方式,效果是一样的,但是呢,第二种方式中,Model可以不用依赖使用BuildContext的provider。WatchContext中的watch方法和ReadContext中的read方法是相似的,但是watch方法会导致widget重构,在获取到值变化的时候,在上面一段代码中,有个Locator类型的参数,它可以让其他对象使用read更加方便。使用watch方法,其实等效于Provider.of(context, listen: true),这个方法只能在StatelessWidget和State.build中使用,如果需要在其他地方使用可以使用Provider.of方法替换,listen需要设置为false,因为它没有这个限制,这个限制也是使用Provider刷新数据时唯一一个异常。

你可能感兴趣的:(Flutter,flutter,provider)