Flutter中Provider的一般用法(二)

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

extension ReadContext on BuildContext {
  T read<T>() {
    return Provider.of<T>(this, listen: false);
  }
}
extension WatchContext on BuildContext {
  T watch<T>() {
    return Provider.of<T>(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<Counter>();
  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<Counter>().increment(),
    },
  );
}

上面这两种方式效果上是一样的,但是后面一种更加健壮。不要在构建widget时,使用read方式去获取一个从不改变的值,即使不会导致widget重构,虽然这个想法是好的,但也不该用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只要是用来解决优化比较脆弱的,依赖层度高的问题,在上面这种情况中,可以考虑使用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<Whatever>(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<Whatever>());
  }
}
// ...
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刷新数据时唯一一个异常。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

释汐宇辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值