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

Flutter GetX异常:新增分类后Rx对象值监听失效

GetX监听失效问题:新增分类后总值不更新,需修改初始输入框才恢复

问题现象

  • 页面首次加载后,输入数值可正常触发总值的监听更新
  • 在DateClass中新增分类并输入金额,总值无变化
  • 修改第一个输入框的数值后,总值正常更新,且后续第二个输入框的变化也能触发总值更新
  • 疑问:是否遗漏了GetX的关键配置?

操作步骤截图

  1. 页面初始化后输入数值,总值正常监听
    页面初始化后输入数值,总值正常监听
  2. 新增分类输入金额,总值未更新
    新增分类输入金额,总值未更新
  3. 修改第一个输入框,总值正常更新
    修改第一个输入框,总值正常更新
  4. 修改第一个数值后,第二个输入框变化触发总值更新
    修改第一个数值后,第二个输入框变化触发总值更新

原代码

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:collection/collection.dart';

class MainClass extends StatefulWidget {
  Function callback;
  MainClass({Key key, this.callback}) : super(key: key);

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

class MainClassState extends State<MainClass> {
  RxList<DateClass> dynamicPerDate = <DateClass>[].obs;

  GlobalKey<MainClassState> mKey = GlobalKey();

  String stringTotal = "";

  addPerDate() async {
    dynamicPerDate.add(DateClass());
    setState(() {});
  }

  calculate() {
    int total = 0;
    List<int> listAmount = [];
    for (int i = 0; i < dynamicPerDate.value.length; i++) {
      for (int j = 0; dynamicPerDate.value[i].listCategory.length > j; j++) {
        var a = dynamicPerDate[i].listCategory[j].amountCategory;
        print(a);

        if (a != null) {
          listAmount.add(a.value);
        }

        total = listAmount.sum;
      }
    }
    return total.toString();
  }

  @override
  void initState() {
    super.initState();
    addPerDate();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: mKey,
      body: Container(
        child: ListView(
          children: [
            ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemCount: dynamicPerDate.length,
                itemBuilder: (_, index) => dynamicPerDate.isNotEmpty
                    ? dynamicPerDate[index]
                    : Container()),
            FlatButton(
                onPressed: () {
                  addPerDate();
                },
                child: Text('Add Date')),
            SizedBox(height: 20),
            Text('Total All Amount From Category Class'),
            Obx(() => Text(calculate())),
          ],
        ),
      ),
    );
  }
}

class DateClass extends StatefulWidget {
  RxList<CategoryClass> listCategory = <CategoryClass>[].obs;
  DateClass({Key key, this.listCategory}) : super(key: key);

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

class _DateClassState extends State<DateClass> {
  addCategoryPerDate() {
    widget.listCategory.add(CategoryClass());
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    widget.listCategory = <CategoryClass>[].obs;
    addCategoryPerDate();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Card(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            Text('Date', style: TextStyle(color: Colors.blue)),
            ListView.builder(
                physics: NeverScrollableScrollPhysics(),
                shrinkWrap: true,
                itemCount: widget.listCategory.length,
                itemBuilder: (_, index) => widget.listCategory.isNotEmpty
                    ? widget.listCategory[index]
                    : Container()),
            FlatButton(
                onPressed: () {
                  addCategoryPerDate();
                },
                child: Text('Add Category'))
          ],
        ),
      ),
    );
  }
}

class CategoryClass extends StatefulWidget {
  RxInt amountCategory = 0.obs;
  TextEditingController amountController;
  CategoryClass({Key key, this.amountCategory}) : super(key: key);

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

class _CategoryClassState extends State<CategoryClass> {
  MainClass mainClass = MainClass();
  @override
  void initState() {
    super.initState();
    widget.amountCategory = 0.obs;
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [
        Text('Amount'),
        TextFormField(
          controller: widget.amountController,
          onChanged: (value) {
            setState(() {
              widget.amountCategory.value = int.parse(value);
              widget.amountCategory.obs;
            });
          },
          keyboardType: TextInputType.number,
        )
      ],
    );
  }
}

问题根源与修复方案

核心问题

  1. Rx对象重复初始化:在DateClass和CategoryClass的initState中重新赋值Rx对象(如widget.listCategory = <CategoryClass>[].obs;),导致MainClass中原来的RxList引用被替换,无法跟踪后续变化。
  2. GetX与setState混用:Rx对象的更新不需要依赖setState,混用会破坏GetX的响应式监听逻辑。
  3. 监听层级缺失:MainClass的Obx仅监听了dynamicPerDate的变化,但未感知内部listCategory的新增和amountCategory的更新(新增的Category的Rx对象未被纳入监听依赖)。

修复步骤

1. 修正DateClass

  • 移除initState中重新赋值widget.listCategory的代码,保留默认初始化或传入的RxList
  • 删除addCategoryPerDate中的setState,直接利用RxList的响应式更新
class DateClass extends StatefulWidget {
  RxList<CategoryClass> listCategory = <CategoryClass>[].obs;
  DateClass({Key key, this.listCategory}) : super(key: key);

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

class _DateClassState extends State<DateClass> {
  addCategoryPerDate() {
    widget.listCategory.add(CategoryClass());
  }

  @override
  void initState() {
    super.initState();
    addCategoryPerDate();
  }

  // build方法不变
}

2. 修正CategoryClass

  • 移除initState中重新赋值widget.amountCategory的代码
  • 移除onChanged中的setState和多余的widget.amountCategory.obs;,直接更新Rx对象的value,同时初始化文本控制器避免空指针
class CategoryClass extends StatefulWidget {
  RxInt amountCategory = 0.obs;
  TextEditingController amountController = TextEditingController();
  CategoryClass({Key key, this.amountCategory}) : super(key: key);

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

class _CategoryClassState extends State<CategoryClass> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [
        Text('Amount'),
        TextFormField(
          controller: widget.amountController,
          onChanged: (value) {
            widget.amountCategory.value = int.tryParse(value) ?? 0;
          },
          keyboardType: TextInputType.number,
        )
      ],
    );
  }
}

3. 修正MainClass

  • 移除addPerDate中的setState,利用RxList的响应式更新
  • 简化calculate方法,确保访问所有Rx对象的.value,让Obx自动收集所有依赖
class MainClassState extends State<MainClass> {
  RxList<DateClass> dynamicPerDate = <DateClass>[].obs;

  GlobalKey<MainClassState> mKey = GlobalKey();

  String stringTotal = "";

  addPerDate() {
    dynamicPerDate.add(DateClass());
  }

  calculate() {
    int total = 0;
    for (final dateItem in dynamicPerDate) {
      for (final categoryItem in dateItem.listCategory) {
        total += categoryItem.amountCategory.value;
      }
    }
    return total.toString();
  }

  // initState和build方法不变
}

修复后效果

  • 新增分类并输入金额时,总值会实时更新
  • 所有输入框的变化都能直接触发总值更新,无需依赖初始输入框的修改

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 11:45:57