Flutter开发问题:计算Datetime差值求和及列表排序
解决方案:Flutter Firebase StreamBuilder 时间差统计与列表排序问题
一、时间差求和与环形进度条实现
1. 定义数据模型与时间差计算函数
先统一数据结构,同时实现单条数据的时间差计算(基于24小时制时间字符串):
class Record { final String id; final String time; // 格式示例:"2024-05-20 09:15" final String time2; Record({required this.id, required this.time, required this.time2}); // 从Firebase文档转模型 factory Record.fromFirebase(Map<String, dynamic> doc) { return Record( id: doc['id'], time: doc['time'], time2: doc['time2'], ); } } // 计算两条时间的差值(单位:分钟) int getDurationMinutes(String timeStr1, String timeStr2) { final time1 = DateTime.parse(timeStr1); final time2 = DateTime.parse(timeStr2); return time1.difference(time2).inMinutes.abs(); }
2. 按ID分组统计总时长
在StreamBuilder中处理数据时,先将Firebase返回的文档转为模型列表,再按ID分组求和:
StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance.collection('your_collection').snapshots(), builder: (context, snapshot) { if (!snapshot.hasData) return const CircularProgressIndicator(); // 转换为模型列表 final List<Record> records = snapshot.data!.docs .map((doc) => Record.fromFirebase(doc.data() as Map<String, dynamic>)) .toList(); // 按ID分组计算总时长 final Map<String, int> idTotalDuration = {}; for (final record in records) { final duration = getDurationMinutes(record.time, record.time2); idTotalDuration[record.id] = (idTotalDuration[record.id] ?? 0) + duration; } // 构建UI... }, );
3. 用环形进度条展示统计结果
使用percent_indicator的CircularPercentIndicator组件,结合分组后的总时长展示进度:
// 假设单ID的目标时长为480分钟(8小时),可根据业务调整 const targetDuration = 480; return ListView.builder( itemCount: idTotalDuration.length, itemBuilder: (context, index) { final currentId = idTotalDuration.keys.elementAt(index); final total = idTotalDuration[currentId]!; final progress = total / targetDuration; return ListTile( title: Text('ID: $currentId'), subtitle: CircularPercentIndicator( radius: 40, lineWidth: 6, percent: progress > 1 ? 1 : progress, // 超过目标时显示100% center: Text('${(progress * 100).toStringAsFixed(0)}%'), progressColor: total >= targetDuration ? Colors.green : Colors.blue, backgroundColor: Colors.grey.shade200, ), ); }, );
二、解决列表乱序问题
Firebase实时数据库默认按文档ID排序,导致新数据可能插入列表中间。只需在转换模型列表后,按时间字段排序即可:
final List<Record> records = snapshot.data!.docs .map((doc) => Record.fromFirebase(doc.data() as Map<String, dynamic>)) .toList() // 按time字段升序排序(最新数据在底部),如需降序则调换a/b位置 ..sort((a, b) => DateTime.parse(a.time).compareTo(DateTime.parse(b.time)));
排序后每次Stream更新,列表都会按时间顺序展示,不会出现数据突然插入中间的情况。
额外提示
- 如果Firebase中存储的是Timestamp类型而非字符串,直接用
doc['time'].toDate()转换为DateTime即可,无需parse。 - 时间差单位可根据需求调整(如
inSeconds/inHours),只需同步修改进度计算逻辑。
内容的提问来源于stack exchange,提问作者Aztur Seritnok
相关产品推荐
相关产品推荐

