Flutter Riverpod疑问:为何两种写法下状态表现不一致?
Riverpod两种watch场景状态差异的原因
两种场景的行为差异,核心是对randomNumberProvider的订阅生命周期不同,再结合Riverpod的Provider缓存机制导致的。
第一种场景:在Widget build顶部订阅Provider
保留以下代码时:
final num = ref.watch(randomNumberProvider); // comment this // ... children: [Text(num), Text(num)], // comment this
MyApp的build方法执行时,第一行就通过ref.watch订阅了randomNumberProvider,只要MyApp这个Widget存在,它就会一直是该Provider的活跃订阅者。- Riverpod的Provider会在有活跃订阅者时,永久缓存其状态值。所以每次
giveBoolProvider变化触发MyApp重新build时,ref.watch直接返回缓存的旧值,不会重新执行RandomNumber的build方法。 - 这就是为什么多次点击按钮,显示的随机数始终不变。
第二种场景:在条件分支内订阅Provider
切换为以下代码时:
// final num = ref.watch(randomNumberProvider); // comment this // ... children: [Text(ref.watch(randomNumberProvider)), Text(ref.watch(randomNumberProvider))], // uncomment this
- 初始状态下,
giveBoolProvider为false,条件分支不执行,此时没有任何代码订阅randomNumberProvider,Riverpod会自动清理它的缓存。 - 第一次点击按钮,
giveBoolProvider变为true,条件分支执行,ref.watch触发订阅。因为缓存已被清理,Provider会执行build方法生成新随机数并缓存。 - 第二次点击按钮,
giveBoolProvider变回false,条件分支被移除,randomNumberProvider的订阅者数量降为0,Riverpod销毁它的缓存。 - 第三次点击按钮,
giveBoolProvider再次变为true,条件分支执行,ref.watch再次触发订阅,但缓存已不存在,Provider只能重新执行build生成新随机数。 - 每次切换
giveBool状态,都会重复“订阅-取消订阅-缓存销毁-重新订阅-重新生成值”的流程,所以每次点击都会看到新的随机数。
额外说明
虽然你在children里写了两次ref.watch(randomNumberProvider),但在同一个build周期内,多次watch同一个Provider会返回同一个缓存值,所以两个Text显示的是同一个随机数,不会出现差异。
内容的提问来源于stack exchange,提问作者wafaa sisalem
相关产品推荐
相关产品推荐

