Flutter中Provider始终获取ValueNotifier旧值的原因排查
为什么Provider.of始终获取旧值?
当ValueNotifier的值变更后,Provider下的Test6组件通过Provider.of获取到的始终是旧值,日志也验证了这一现象,具体原因及解决办法如下:
问题代码
class Test6 extends StatelessWidget { const Test6({Key? key}) : super(key: key); @override Widget build(BuildContext context) { int value = Provider.of<int>(context, listen: false); print('Test6 read from Provider.of:$value'); return Text('$value'); } } class Test5 extends StatelessWidget { Test5({Key? key}) : super(key: key); ValueNotifier<int> notifier = ValueNotifier(1); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ ValueListenableBuilder( valueListenable: notifier, builder: (_, value, __) { print(' _value:$value'); int intValue = notifier.value; print(' notifier.value:$value'); return Provider( create: (context) => intValue, child: Test6(), ); }), TextButton( onPressed: () { print('Test5 change notifier'); notifier.value = notifier.value + 1; print('Test5 after :$notifier'); }, child: Text('add 1')), ], ); } }
日志信息
flutter: _value:1 flutter: notifier.value:1 flutter: Test6 read from Provider.of:1 flutter: Test5 change notifier flutter: Test5 after :ValueNotifier<int>#d2647(2) flutter: _value:2 flutter: notifier.value:2 flutter: Test6 read from Provider.of:1
原因分析
核心问题出在**Provider的复用机制**:
Provider的create回调只会在Provider第一次被创建时执行一次,后续Element树中若存在同类型的Provider,Flutter会直接复用这个实例,不会重新执行create。- 当ValueNotifier值变化时,ValueListenableBuilder会触发重建,你虽然在builder里返回了新的Provider,但因为它的类型是
int,Flutter会复用之前的Provider实例,导致create里的intValue始终是第一次注入的1,不会更新。 - 另外Test6中使用
listen: false,即使Provider后续有更新(如果有的话),Test6也不会触发重建,但这里根本问题是Provider本身就没有更新内部的值。
解决办法
推荐方案:用ChangeNotifierProvider管理可变状态
把ValueNotifier交给ChangeNotifierProvider管理,让Test6直接监听或读取ValueNotifier的实时值:// 修改Test5中的Provider部分 ChangeNotifierProvider.value( value: notifier, child: Test6(), ) // 修改Test6 @override Widget build(BuildContext context) { var notifier = Provider.of<ValueNotifier<int>>(context); int value = notifier.value; print('Test6 read from Provider.of:$value'); return Text('$value'); }临时方案:给Provider添加唯一Key
通过给Provider设置ValueKey(intValue),让Flutter认为每次值变化都是新的Provider,从而重新执行create回调:Provider( key: ValueKey(intValue), create: (context) => intValue, child: Test6(), )注意:这种方式会每次重建Provider和子组件,不是最优的状态管理方式,仅作临时解决。
内容的提问来源于stack exchange,提问作者ximmyxiao
相关产品推荐
相关产品推荐

