You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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本身就没有更新内部的值。

解决办法

  1. 推荐方案:用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');
    }
    
  2. 临时方案:给Provider添加唯一Key
    通过给Provider设置ValueKey(intValue),让Flutter认为每次值变化都是新的Provider,从而重新执行create回调:

    Provider(
      key: ValueKey(intValue),
      create: (context) => intValue,
      child: Test6(),
    )
    

    注意:这种方式会每次重建Provider和子组件,不是最优的状态管理方式,仅作临时解决。

内容的提问来源于stack exchange,提问作者ximmyxiao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 12:57:57