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

Flutter中Model类意外更新:如何阻止未保存时的数值修改?

解决Model类意外更新的问题

我帮你排查了代码里的问题,核心原因是Dart中自定义类属于引用类型:你直接把myNumber实例传给模态框时,模态框里的_newNumber和主页面的myNumber指向的是同一个对象内存地址——所以哪怕你没点保存,修改_newNumber.number的时候,原对象的数值也会跟着变。

修复方案:创建对象副本

我们需要在打开模态框时,创建原NumberClass对象的独立副本,让模态框操作副本,这样原对象就不会被意外修改,只有用户点击保存时,再用副本替换原对象。

修改后的完整代码

class MaterialScreen extends StatefulWidget {
  @override
  _MaterialScreenState createState() => _MaterialScreenState();
}

NumberClass myNumber = NumberClass(0);

class _MaterialScreenState extends State<MaterialScreen> {
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FlatButton(
          child: Text('Open'),
          onPressed: () async {
            NumberClass n = await showModalBottomSheet(
              context: context,
              builder: (_) => Modal(
                number: myNumber,
              ),
            );
            if (n != null) {
              setState(() {
                myNumber = n;
              });
            }
          },
        ),
      ),
    );
  }
}

class Modal extends StatefulWidget {
  final NumberClass number;
  const Modal({this.number});

  @override
  _ModalState createState() => _ModalState();
}

class _ModalState extends State<Modal> {
  NumberClass _newNumber; // 把变量移到State类内部,避免全局变量污染

  @override
  void initState() {
    // 关键:创建原对象的独立副本,而非直接赋值引用
    _newNumber = NumberClass(widget.number.number);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(_newNumber.number.toString()),
            FlatButton(
              child: Text('Update'),
              onPressed: () {
                setState(() {
                  _newNumber.number = _newNumber.number + 1;
                });
                print(_newNumber.number);
                print(widget.number.number); // 现在这里会保持原数值不变
              },
            )
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            OutlineButton(
              child: Text('close - dont save'),
              onPressed: () => Navigator.pop(context),
            ),
            OutlineButton(
              child: Text('close - save'),
              onPressed: () => Navigator.pop(context, _newNumber),
            ),
          ],
        )
      ],
    );
  }
}

class NumberClass {
  int number;
  NumberClass(this.number);

  // 可选扩展:添加copyWith方法,后续新增属性时更优雅地创建副本
  NumberClass copyWith({int number}) {
    return NumberClass(number ?? this.number);
  }
}

关键修改点说明

  • 创建独立副本:在Modal的initState里,我们用NumberClass(widget.number.number)生成了新实例,让_newNumber和原对象彻底独立,修改副本不会影响原对象。
  • 移除全局变量:把_newNumber移到_ModalState内部,避免全局变量带来的意外状态干扰。
  • 可选copyWith方法:如果后续NumberClass需要添加更多属性,用copyWith可以更简洁地创建副本,比如_newNumber = widget.number.copyWith()。

修改后,模态框内的操作只会影响副本:点击“close - save”才会用副本更新主页面,点击“close - dont save”则直接关闭,原数值完全保留,重新打开模态框时会再次基于原对象创建新副本,显示修改前的状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 09:27:30