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

Flutter使用Health库获取Google Fit/Apple Healthkit完整睡眠数据方法

问题处理方案

Health 2.0.5版本对接Google Fit、Apple HealthKit时不会直接返回聚合后的睡眠周期核心字段,两个平台原生输出的就是离散的睡眠阶段分段,入睡时间、起床时间、睡眠总时长这类指标需要按通用规则自行聚合计算。

第一步:数据预处理

先对拉取到的HealthDataPoint列表做过滤排序:

  • 仅保留类型前缀为HealthDataType.SLEEP的睡眠相关数据点,过滤掉运动、心率等无关数据
  • 所有数据点按dateFrom字段做升序排列,保证时间线连贯
  • 过滤时长小于5分钟的极短分段,这类基本是传感器误判产生的脏数据

第二步:独立睡眠周期划分

两个平台不会主动标记单次睡眠的起止边界,按行业通用规则划分即可:

  • 设定相邻分段间隔阈值为30分钟:如果两个相邻睡眠分段的时间间隔超过30分钟,判定为属于两个独立的睡眠周期
  • 该阈值可根据业务需求微调,和Google Fit、Apple Health自带的睡眠统计逻辑基本对齐
  • 不需要手动按自然日切割数据,跨零点的连续睡眠分段会自动归为同一个周期,符合实际睡眠场景

第三步:核心指标计算规则

对划分完成的单个睡眠周期内的所有有效分段,按以下规则提取指标:

  • 入睡时间:周期内第一个有效分段的dateFrom值
  • 起床时间:周期内最后一个有效分段的dateTo值
  • 在床总时长:起床时间和入睡时间的差值
  • 实际睡眠总时长:累加周期内所有非SLEEP_AWAKE类型分段的时长(即dateTo.difference(dateFrom)的总和),排除醒着躺在床上的时间
  • 可选扩展指标:睡眠效率=实际睡眠总时长/在床总时长,各睡眠阶段占比=对应阶段时长/实际睡眠总时长

可复用实现代码

// 睡眠周期数据模型
class SleepSession {
  final DateTime fallAsleepTime;
  final DateTime wakeUpTime;
  final Duration totalSleepDuration;
  final Duration totalInBedDuration;
  final List<HealthDataPoint> stagePoints;

  SleepSession({
    required this.fallAsleepTime,
    required this.wakeUpTime,
    required this.totalSleepDuration,
    required this.totalInBedDuration,
    required this.stagePoints,
  });
}

// 核心聚合逻辑
List<SleepSession> parseSleepSessions(List<HealthDataPoint> allHealthPoints) {
  // 过滤排序睡眠数据
  List<HealthDataPoint> sleepPoints = allHealthPoints
      .where((p) => p.type.toString().startsWith('HealthDataType.SLEEP'))
      .toList()
    ..sort((a, b) => a.dateFrom.compareTo(b.dateFrom));

  const Duration gapThreshold = Duration(minutes: 30);
  const Duration minValidPointDuration = Duration(minutes: 5);
  List<SleepSession> sessions = [];
  List<HealthDataPoint> currentSessionPoints = [];

  for (final point in sleepPoints) {
    final pointDuration = point.dateTo.difference(point.dateFrom);
    // 过滤无效短分段
    if (pointDuration < minValidPointDuration) continue;

    if (currentSessionPoints.isEmpty) {
      currentSessionPoints.add(point);
      continue;
    }

    // 校验相邻分段间隔
    final lastPointInSession = currentSessionPoints.last;
    final timeGap = point.dateFrom.difference(lastPointInSession.dateTo);
    if (timeGap > gapThreshold) {
      // 间隔超限,生成上一个周期
      sessions.add(_buildSessionFromPoints(currentSessionPoints));
      currentSessionPoints = [point];
    } else {
      currentSessionPoints.add(point);
    }
  }

  // 补全最后一个周期
  if (currentSessionPoints.isNotEmpty) {
    sessions.add(_buildSessionFromPoints(currentSessionPoints));
  }

  return sessions;
}

// 单周期构建方法
SleepSession _buildSessionFromPoints(List<HealthDataPoint> points) {
  final fallAsleepTime = points.first.dateFrom;
  final wakeUpTime = points.last.dateTo;
  final totalInBedDuration = wakeUpTime.difference(fallAsleepTime);
  Duration totalSleepDuration = Duration.zero;

  for (final p in points) {
    // 清醒阶段不计入实际睡眠时长
    if (p.type != HealthDataType.SLEEP_AWAKE) {
      totalSleepDuration += p.dateTo.difference(p.dateFrom);
    }
  }

  return SleepSession(
    fallAsleepTime: fallAsleepTime,
    wakeUpTime: wakeUpTime,
    totalSleepDuration: totalSleepDuration,
    totalInBedDuration: totalInBedDuration,
    stagePoints: points,
  );
}

兼容提示:2.0.5版本Health库在部分Android机型上拉取SLEEP_ASLEEP类型时,会直接返回合并后的总睡眠时长,不会返回细分阶段,iOS端默认返回全部分段数据,实际使用时可根据平台返回的类型做时长去重,避免重复计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 16:27:40