Flutter中同时读取Firebase两个集合并对比销售额的问题
解决方案
1. 添加依赖
首先在pubspec.yaml中添加rxdart依赖,用于合并两个Firebase数据流:
dependencies: flutter: sdk: flutter rxdart: ^0.27.7 cloud_firestore: ^4.12.0 # 确保与你的Firestore版本兼容
2. 修改数据流读取逻辑
保留原有单个集合读取方法,新增合并两个集合流的方法,同时获取当前选择月份和目标对比月份(示例为10.2022 Sales)的数据:
import 'package:rxdart/rxdart.dart'; // 原单个集合读取方法 static Stream<List<showSummaryReport>> read(String type) { final serviceCollection = FirebaseFirestore.instance.collection(type); return serviceCollection.snapshots().map((querySnapshot) => querySnapshot.docs.map((e) => showSummaryReport.fromSnapshot(e)).toList()); } // 新增合并两个集合流的方法 static Stream<List<List<showSummaryReport>>> readTwoCollections(String currentCollection, String compareCollection) { final currentStream = read(currentCollection); final compareStream = read(compareCollection); // 合并两个流,任一数据流更新时同步获取最新数据 return CombineLatestStream.list([currentStream, compareStream]); }
3. 修改StreamBuilder与UI展示
替换原单个流的监听逻辑,改为监听合并后的流,并匹配对应服务的对比数据:
Visibility( visible: gotChoice, child: StreamBuilder<List<List<showSummaryReport>>>( // 传入当前月份和对比月份的集合名称 stream: readTwoCollections('${widget.expChoice.toString()} Sales', '10.2022 Sales'), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text("出现错误")); } if (snapshot.hasData) { final currentData = snapshot.data![0]; // 当前月份数据 final compareData = snapshot.data![1]; // 对比月份数据 return Expanded( child: ListView.builder( itemCount: currentData.length, itemBuilder: (context, index) { final service = currentData[index]; // 根据服务名称匹配对比数据中的对应条目 final compareService = compareData.firstWhere( (item) => item.serviceName == service.serviceName, orElse: () => showSummaryReport(serviceName: service.serviceName, sales: 0), // 未匹配到的默认值 ); return Column( children: [ ListTile( onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context)=> selectedDetailReport(detail: '${service.serviceName} Sales', month: widget.expChoice.toString(),))); }, title: Card( color: 'CAF0F8'.toColor(), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), child: Table( children: [ TableRow(children: [SizedBox(height: 10,), SizedBox(height: 10,)]), TableRow( children: [ Padding( padding: const EdgeInsets.fromLTRB(30,0,0,0), child: Text('服务名称 :',style: TextStyle(fontFamily: 'MonSemi', fontSize: 17)), ), Center(child: Text(service.serviceName.toString(),style: TextStyle(fontFamily: 'MonSemi', fontSize: 17))), ] ), TableRow(children: [SizedBox(height: 10,), SizedBox(height: 10,)]), TableRow( children: [ Padding( padding: const EdgeInsets.fromLTRB(30,0,0,0), child: Text('当月销售额 :',style: TextStyle(fontFamily: 'MonSemi', fontSize: 17)), ), Center(child: Text(service.sales.toString(),style: TextStyle(fontFamily: 'MonSemi', fontSize: 17))), ] ), TableRow(children: [SizedBox(height: 10,), SizedBox(height: 10,)]), TableRow( children: [ Padding( padding: const EdgeInsets.fromLTRB(30,0,0,0), child: Text('对比月份销售额 :',style: TextStyle(fontFamily: 'MonSemi', fontSize: 17)), ), // 展示对比数据的销售额 Center(child: Text(compareService.sales.toString(),style: TextStyle(fontFamily: 'MonSemi', fontSize: 17))), ] ), TableRow(children: [SizedBox(height: 10,), SizedBox(height: 10,)]), ], ), ), ), ], ); }), ); } return Center(child: CircularProgressIndicator()); }), ),
关键改动说明
- 用
CombineLatestStream合并两个Firestore数据流,保证任一集合数据更新时UI同步刷新 - 通过
firstWhere按服务名称匹配两个集合的数据,实现同服务销售额对比 - 为未匹配到的数据设置默认值,避免UI报错
- 替换原占位文本为对比月份的实际销售额数据
内容的提问来源于stack exchange,提问作者Lim
相关产品推荐
相关产品推荐

