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
相关产品推荐
相关产品推荐

