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

StreamBuilder中GetX Obx组件失效问题求助

问题:StreamBuilder中使用Obx展示GetX变量更新值时出错

我用StreamBuilder从Firebase拉取数据,用GetX做状态管理,想在StreamBuilder里展示GetX Controller中初始化变量的更新值,但用Obx组件时出现错误,相关代码如下:

GetX Controller代码

RxBool check = false.obs;
var itemArr = [].obs;
RxDouble itemSum = 0.0.obs;

updateitemSum(double value) {
  itemSum.value = value;
}

RxDouble getItemSum() {
  return itemSum;
}

updateCheck() {
  check.value = true;
  print('value is: ');
  print(check);
}

主页面代码

StreamBuilder(
  stream: FirebaseFirestore.instance
      .collection('Item')
      .where('uid', isEqualTo: uid)
      .snapshots(),
  builder: (BuildContext context,
      AsyncSnapshot<QuerySnapshot> snapshot) {
    if (snapshot.hasData && snapshot.data?.docs.length != 0) {
      return ListView.builder(
            shrinkWrap: true,
            physics: NeverScrollableScrollPhysics(),
            itemCount: snapshot.data?.docs.length,
            itemBuilder: (context, index) {
              _controller.updateitemSum(0.0);
              DocumentSnapshot itemTittle = snapshot.data
                  ?.docs[index] as DocumentSnapshot<Object?>;
              return Container(
                width: MediaQuery.of(context).size.width * 0.9,
                child: Card(
                  elevation: 6.0,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 8.0),
                    child: Column(
                      children: [
                        Container(
                          height: MediaQuery.of(context)
                                  .size
                                  .height *
                              0.1,
                          child: Column(
                            children: [
                              Row(
                                mainAxisAlignment:
                                    MainAxisAlignment
                                        .spaceBetween,
                                children: [
                                  Container(
                                    child: Padding(
                                      padding:
                                          const EdgeInsets.all(
                                              6.0),
                                      child: Text(
                                        itemTittle['tittle'],
                                        style: TextStyle(
                                            fontWeight:
                                                FontWeight.bold,
                                            fontSize: 18,
                                            color: Colors
                                                .lightBlue),
                                      ),
                                    ),
                                  ),
                                  Container(
                                    child: Padding(
                                      padding:
                                          const EdgeInsets.all(
                                              6.0),
                                      child: Text(
                                        'Income',
                                        style: TextStyle(
                                            fontWeight:
                                                FontWeight.bold,
                                            fontSize: 18,
                                            color: Colors
                                                .lightBlue),
                                      ),
                                    ),
                                  ),
                                ],
                              ),
                              Divider(
                                color: Colors.grey,
                                height: 25,
                                thickness: 1,
                              )
                            ],
                          ),
                        ),
                        StreamBuilder(
                            stream: FirebaseFirestore.instance
                                .collection('Income')
                                .where('uid', isEqualTo: uid)
                                .where('itittle',
                                    isEqualTo:
                                        itemTittle['tittle'])
                                .where('date',
                                    isEqualTo: DateFormat()
                                        .add_yMMM()
                                        .format(selectedDate!))
                                .snapshots(),
                            builder: (BuildContext context,
                                AsyncSnapshot<QuerySnapshot>
                                    snapshot) {
                              if (snapshot.hasData) {
                                return ListView.builder(
                                    shrinkWrap: true,
                                    physics:
                                        NeverScrollableScrollPhysics(),
                                    itemCount: snapshot
                                        .data?.docs.length,
                                    itemBuilder:
                                        (context, index) {
                                      DocumentSnapshot ds =
                                          snapshot.data?.docs[index] as DocumentSnapshot<Object?>;
                                      double sum = 0.0;
                                      sum = double.parse(_controller.getItemSum().toString()) +
                                              double.parse(
                                                  ds['income']);
                                      _controller.updateitemSum(sum);

                                        print('itemsum is:');
                                        print(_controller.itemSum);
                                      return Column(
                                        children: [
                                          Row(
                                            mainAxisAlignment:
                                                MainAxisAlignment
                                                    .spaceBetween,
                                            children: [
                                              Container(
                                                child: Padding(
                                                  padding:
                                                      const EdgeInsets
                                                              .all(
                                                          6.0),
                                                  child: Text(
                                                    ds['name'],
                                                    style: TextStyle(
                                                        fontSize: 14,
                                                        color: Colors.black),
                                                  ),
                                                ),
                                              ),
                                              Container(
                                                child: Padding(
                                                  padding:
                                                      const EdgeInsets
                                                              .all(
                                                          6.0),
                                                  child: Text(
                                                    ds['income'],
                                                    style: TextStyle(
                                                        fontSize: 14,
                                                        color: Colors.black),
                                                  ),
                                                ),
                                              ),
                                            ],
                                          ),
                                          Divider(
                                            color: Colors.grey,
                                            height: 25,
                                            thickness: 1,
                                          )
                                        ],
                                      );
                                    });
                              } else {
                                return Container();
                              }
                            }),
                         Container(
                            height: MediaQuery.of(context)
                                    .size
                                    .height * 0.1,
                            child: Row(
                              mainAxisAlignment:
                                  MainAxisAlignment
                                      .spaceBetween,
                              children: [
                                Container(
                                  child: TextButton(
                                    onPressed: () {
                                      Navigator.push(
                                          context,
                                          MaterialPageRoute(
                                              builder: (context) =>
                                                  AddIncome(
                                                      itemtittle:
                                                          itemTittle[
                                                              'tittle'])));
                                    },
                                    child: Text(
                                      'Add Income',
                                      style: TextStyle(
                                          fontWeight:
                                              FontWeight.bold,
                                          fontSize: 14,
                                          color:
                                              Colors.lightBlue),
                                    ),
                                  ),
                                ),
                                Container(
                                  child: Padding(
                                    padding:
                                        const EdgeInsets.all(
                                            6.0),
                                    child: Obx(()=>
                                        Text(
                                          _controller.itemSum.toString(),
                                          style: TextStyle(
                                              fontWeight:
                                              FontWeight.bold,
                                              fontSize: 14,
                                              color: Colors.black),
                                        )
                                    ),
                                  ),
                                ),
                              ],
                            ),
                          ),

                      ],
                    ),
                  ),
                ),
              );
            });
    } else {
      return Container(
        width: MediaQuery.of(context).size.width * 0.9,
        height: MediaQuery.of(context).size.height * 0.2,
        child: Card(
          elevation: 6.0,
          child: Padding(
            padding:
                const EdgeInsets.symmetric(horizontal: 8.0),
            child: Column(
              children: [
                Container(
                  height:
                      MediaQuery.of(context).size.height * 0.1,
                  child: Column(
                    children: [
                      Row(
                        mainAxisAlignment:
                            MainAxisAlignment.spaceBetween,
                        children: [
                          Container(
                            child: Padding(
                              padding:
                                  const EdgeInsets.all(6.0),
                              child: Text(
                                'My Income',
                                style: TextStyle(
                                    fontWeight: FontWeight.bold,
                                    fontSize: 18,
                                    color: Colors.lightBlue),
                              ),
                            ),
                          ),
                          Container(
                            child: Padding(
                              padding:
                                  const EdgeInsets.all(6.0),
                              child: Text(
                                'Income',
                                style: TextStyle(
                                    fontWeight: FontWeight.bold,
                                    fontSize: 18,
                                    color: Colors.lightBlue),
                              ),
                            ),
                          ),
                        ],
                      ),
                      Divider(
                        color: Colors.grey,
                        height: 25,
                        thickness: 1,
                      )
                    ],
                  ),
                ),
                Container(
                  child: Row(
                    mainAxisAlignment:
                        MainAxisAlignment.spaceBetween,
                    children: [
                      Container(
                        child: TextButton(
                          onPressed: () async {
                            ToastMessage("Wait");
                            Navigator.push(
                                context,
                                MaterialPageRoute(
                                    builder: (context) =>
                                        AddIncome(
                                            itemtittle:
                                                'My Income')));
                            await itemT.doc().set({
                              "uid": uid,
                              'tittle': 'My Income',
                            });
                            await itemT.doc().set({
                              "uid": uid,
                              'tittle': 'Expense',
                            });
                          },
                          child: Text(
                            'Add Income',
                            style: TextStyle(
                                fontWeight: FontWeight.bold,
                                fontSize: 14,
                                color: Colors.lightBlue),
                          ),
                        ),
                      ),
                      Container(
                        child: Padding(
                          padding: const EdgeInsets.all(1.0),
                          child: Text(
                            "0.0",
                            style: TextStyle(
                                fontWeight: FontWeight.bold,
                                fontSize: 14,
                                color: Colors.black),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      );
    }
  }),

错误信息(翻译后)

[GetX] 检测到GetX的不当使用。你应该只在需要更新的特定Widget中使用GetX或Obx。如果你看到这个错误,可能是你没有将任何可观察变量放入GetX/Obx中,或者将它们放在了GetX认为不适合更新的范围之外。
正确用法示例:
Obx(() => Text("${controller.count}"));
或者:
GetX(
init: Controller(),
builder: (controller) => Text("${controller.count}"),
);

解决方案

1. 修复变量共享导致的状态混乱

原代码中所有Item共用同一个itemSum变量,多个StreamBuilder同时修改会引发状态冲突。修改Controller,用Map为每个Item单独维护总和:

class YourController extends GetxController {
  RxBool check = false.obs;
  var itemArr = [].obs;
  // 存储每个Item标题对应的总和
  RxMap<String, double> itemSums = <String, double>{}.obs;

  // 更新指定Item的总和
  updateItemSum(String itemTitle, double value) {
    itemSums[itemTitle] = value;
  }

  // 获取指定Item的总和,默认返回0.0
  double getItemSum(String itemTitle) {
    return itemSums[itemTitle] ?? 0.0;
  }

  updateCheck() {
    check.value = true;
    print('value is: ');
    print(check);
  }
}

2. 修正总和计算逻辑

在内层StreamBuilder中,先一次性计算当前Item的总金额,再更新到Controller,避免重复计算:

// 内层StreamBuilder的builder部分
if (snapshot.hasData) {
  double total = 0.0;
  // 遍历所有文档计算总和
  for (var doc in snapshot.data!.docs) {
    total += double.parse(doc['income']);
  }
  // 更新当前Item的总和
  _controller.updateItemSum(itemTittle['tittle'], total);

  return ListView.builder(
    shrinkWrap: true,
    physics: NeverScrollableScrollPhysics(),
    itemCount: snapshot.data?.docs.length,
    itemBuilder: (context, index) {
      DocumentSnapshot ds = snapshot.data?.docs[index] as DocumentSnapshot<Object?>;
      return Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Container(
                child: Padding(
                  padding: const EdgeInsets.all(6.0),
                  child: Text(
                    ds['name'],
                    style: TextStyle(fontSize: 14, color: Colors.black),
                  ),
                ),
              ),
              Container(
                child: Padding(
                  padding: const EdgeInsets.all(6.0),
                  child: Text(
                    ds['income'],
                    style: TextStyle(fontSize: 14, color: Colors.black),
                  ),
                ),
              ),
            ],
          ),
          Divider(color: Colors.grey, height: 25, thickness: 1)
        ],
      );
    });
}

3. 修正Obx的使用

展示总和时,根据当前Item的标题获取对应的值:

Obx(()=>
  Text(
    _controller.getItemSum(itemTittle['tittle']).toString(),
    style: TextStyle(
        fontWeight: FontWeight.bold,
        fontSize: 14,
        color: Colors.black),
  )
),

4. 移除不必要的全局重置

删除外层ListView.builder中的_controller.updateitemSum(0.0);,现在每个Item的总和单独维护,无需全局重置。

关键原因分析

  • 多个Item共用同一个itemSum变量,导致状态更新混乱,Obx无法正确追踪单一变量的变化。
  • 总和计算放在ListView的itemBuilder中,每个列表项都会执行一次计算,导致变量被频繁覆盖,触发不必要的刷新,最终引发GetX的使用错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 01:01:56