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

Provider(ChangeNotifier)内Stateful组件notifyListeners后不更新问题

问题场景
  • 使用搭配ChangeNotifier模型的Stateless类型Provider组件做状态管理,Provider内部嵌套了Stateful子组件
  • 异常现象:调用notifyListeners()方法时,Stateless组件内除Stateful子组件外的所有子组件均正常更新
  • 复现表现:点击触发添加的按钮后,期望输出First: The value is 1st,实际输出为First: The value is 2nd
  • 方案约束:生产环境将整个Provider类重构为Stateful组件的实现成本较高,不采用该方案
原始复现代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Model extends ChangeNotifier {
  final List<ListElement> elements;
  Model({required this.elements});

  void add() {
    elements.insert(0, ListElement(name: "First", value: "1st"));
    notifyListeners();
  }
}


class ListElement {
  final String name;
  var value;
  ListElement({required this.name, required this.value});
}

class ValueWidget extends StatefulWidget {
  final String value;
  ValueWidget({required this.value});

  @override
  State<StatefulWidget> createState() => _ValueWidget(value: value);

}

class _ValueWidget extends State<ValueWidget> {

  String value;
  _ValueWidget({required this.value});

  @override
  Widget build(BuildContext context) {
    return Text("The value is ${value}.");
  }

}

class StatelessPage extends StatelessWidget {
  
  final model = Model(elements: [
    ListElement(name: "Second", value: "2nd"),
      ListElement(name: "Third", value: "3rd")]);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ChangeNotifierProvider(
        create: (context) => model,
        child: ConsumerWidget())
    );
  }
}

class ConsumerWidget extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Consumer<Model>(builder: (context, model, _) {
      return SingleChildScrollView(
        child: Container(
          padding: EdgeInsets.fromLTRB(10, 30, 10, 10000),
          child: Column(
            children: [Column(
                children: model.elements.map((element) {
                  return Row(children: [
                    Text("${element.name}: "),
                    ValueWidget(value: element.value)
                  ]);
                }).toList(),
            ),
              TextButton(onPressed: model.add,
                  child: Text("Add element to beginning")),
            ],
          ),
        ),
      );
    });
  }
}
问题根因

Flutter的组件更新采用增量对比机制:新旧组件树对比时,对于StatefulWidget节点,如果其构造传入的属性值没有发生变化,框架会判定该组件无需重建,对应的State对象会被直接复用。
原代码中ValueWidget对应的State类将传入的value存储为自身成员变量,仅在State初始化时完成一次赋值;当列表头部插入新元素后,对应位置的StatefulWidget节点被复用,State中缓存的旧值没有同步更新,最终显示错误内容。

修复方案

不要将依赖全局状态的业务数据直接传入StatefulWidget后缓存到State成员变量中,改为传入数据对应的索引标识,在build方法执行阶段直接从监听的Model中读取最新数据,确保每次状态更新触发重建时都能获取到最新值。

修复后可运行代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Model extends ChangeNotifier {
  final List<ListElement> elements;
  Model({required this.elements});

  void add() {
    elements.insert(0, ListElement(name: "First", value: "1st"));
    notifyListeners();
  }
}


class ListElement {
  final String name;
  var value;
  ListElement({required this.name, required this.value});
}

class ValueWidget extends StatefulWidget {
  final int ind;
  final Model model;
  ValueWidget({required this.ind, required this.model});

  @override
  State<StatefulWidget> createState() => _ValueWidget(
      ind: ind, model: model);
}

class _ValueWidget extends State<ValueWidget> {

  final int ind;
  final Model model;
  _ValueWidget({required this.ind, required this.model});

  @override
  Widget build(BuildContext context) {
    // 也可以直接通过Provider获取model,无需从上层传参
    // final model = Provider.of<Model>(context, listen: true);
    // 每次build执行时重新读取数据,是组件能正确响应更新的核心
    final element = model.elements[ind];
    return Text("The value is ${element.value}.");
  }
}

class StatelessPage extends StatelessWidget {
  
  final model = Model(
      elements: [
    ListElement(name: "Second", value: "2nd"),
      ListElement(name: "Third", value: "3rd")]
  );
  
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      body: ChangeNotifierProvider(
        create: (context) => model,
        child: ConsumerWidget())
    );
  }
}

class ConsumerWidget extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Consumer<Model>(builder: (context, model, _) {
      return SingleChildScrollView(
        child: Container(
          padding: EdgeInsets.fromLTRB(10, 30, 10, 10000),
          child: Column(
            children: [Column(
                children:
                  model.elements.asMap().entries.map((ele) {
                  return Row(children: [
                    Text("${ele.value.name}: "),
                    ValueWidget(ind: ele.key, model: model),
                  ]);
                }).toList(),
            ),
              TextButton(onPressed: model.add,
                  child: Text("Add element to beginning")),
            ],
          ),
        ),
      );
    });
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:24:25