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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:55:46