在这里插入图片描述

上周,奶奶跟我抱怨说:"最近总是睡不好,半夜醒来好几次,也不知道自己到底睡了多久。"听到这话,我意识到很多老年人都有睡眠问题,但他们往往不知道如何科学地记录和分析自己的睡眠状况。于是,我决定为他们开发一个简单实用的睡眠监测功能。

在与多位老年用户交流后,我发现他们最需要的不是复杂的睡眠分期图表,而是能看懂的睡眠时长简单的睡眠质量评价,以及实用的改善建议。基于这些真实需求,我们设计了这套睡眠监测系统。

睡眠数据模型设计

在设计睡眠数据模型时,我参考了国际睡眠医学会的标准,同时结合老年人的实际使用场景,确定了核心的数据结构。

首先定义睡眠记录的基础模型:

class SleepRecord {
  final String id;
  final DateTime bedTime;
  final DateTime wakeTime;
  final int duration;
  final int deepSleepDuration;
  final int lightSleepDuration;
  final int awakeCount;
  final String quality;
  final String? note;
  
  SleepRecord({
    required this.id,
    required this.bedTime,
    required this.wakeTime,
    required this.duration,
    required this.deepSleepDuration,
    required this.lightSleepDuration,
    required this.awakeCount,
    required this.quality,
    this.note,
  });
  
  double get sleepEfficiency {
    final totalTime = wakeTime.difference(bedTime).inMinutes;
    return (duration / totalTime * 100).clamp(0, 100);
  }
}

这个模型涵盖了睡眠监测的核心数据。bedTimewakeTime 记录睡眠的起止时间,duration 存储实际睡眠时长,awakeCount 记录夜间醒来次数。sleepEfficiency 方法自动计算睡眠效率,这是评估睡眠质量的重要指标。

💤 数据设计的实用性考虑:我们没有采用复杂的睡眠分期(REM、NREM等),而是简化为深度睡眠和浅度睡眠两种,这样老年用户更容易理解。睡眠效率的计算公式参考了医学标准,但用百分比表示,更加直观。

创建睡眠统计模型:

class SleepStatistics {
  final double averageDuration;
  final double averageEfficiency;
  final int totalRecords;
  final String trend;
  
  SleepStatistics({
    required this.averageDuration,
    required this.averageEfficiency,
    required this.totalRecords,
    required this.trend,
  });
  
  String get qualityDescription {
    if (averageDuration >= 420 && averageEfficiency >= 85) {
      return '睡眠质量优秀';
    } else if (averageDuration >= 360 && averageEfficiency >= 75) {
      return '睡眠质量良好';
    } else if (averageDuration >= 300) {
      return '睡眠质量一般';
    } else {
      return '睡眠质量较差';
    }
  }
}

统计模型用于展示一段时间内的睡眠情况。平均睡眠时长平均睡眠效率是两个关键指标,趋势分析帮助用户了解睡眠状况的变化。

📊 统计数据的人性化表达:我们参考了《中国成人失眠诊断与治疗指南》,将7小时(420分钟)作为优秀睡眠的标准。但考虑到老年人的实际情况,6小时(360分钟)也被认为是良好的。

睡眠监测控制器

创建核心控制器:

class SleepController extends GetxController {
  final RxList<SleepRecord> records = <SleepRecord>[].obs;
  final Rx<SleepStatistics?> statistics = Rx<SleepStatistics?>(null);
  final RxBool isLoading = false.obs;
  
  
  void onInit() {
    super.onInit();
    loadSleepData();
  }
  
  Future<void> loadSleepData() async {
    isLoading.value = true;
    final storedRecords = await SleepStorage.loadRecords();
    records.value = storedRecords;
    if (records.isEmpty) await _createSampleData();
    _calculateStatistics();
    isLoading.value = false;
  }
}

控制器负责管理所有睡眠数据。首次使用时提供示例数据,帮助用户快速理解功能。

⚡ 数据初始化考虑:示例数据使用真实睡眠场景,让老年用户更容易理解。

实现添加睡眠记录:

void addSleepRecord({
  required DateTime bedTime,
  required DateTime wakeTime,
  int? awakeCount,
  String? note,
}) {
  final totalMinutes = wakeTime.difference(bedTime).inMinutes;
  final quality = _evaluateSleepQuality(totalMinutes, awakeCount ?? 0);
  
  final record = SleepRecord(
    id: DateTime.now().millisecondsSinceEpoch.toString(),
    bedTime: bedTime,
    wakeTime: wakeTime,
    duration: totalMinutes,
    deepSleepDuration: (totalMinutes * 0.4).round(),
    lightSleepDuration: (totalMinutes * 0.6).round(),
    awakeCount: awakeCount ?? 0,
    quality: quality,
    note: note,
  );
  
  records.insert(0, record);
  SleepStorage.saveRecords(records);
  Get.snackbar('记录成功', '睡眠质量:$quality');
}

String _evaluateSleepQuality(int duration, int awakeCount) {
  if (duration >= 420 && awakeCount <= 1) return '优秀';
  if (duration >= 360 && awakeCount <= 2) return '良好';
  if (duration >= 300 && awakeCount <= 3) return '一般';
  return '较差';
}

系统自动计算睡眠时长和评估质量。深度睡眠占40%,这是简化算法,但对日常监测足够。

🎯 算法实用性权衡:采用简单规则判断,结果可解释性强,老年用户能理解评价依据。

实现统计计算:

void _calculateStatistics() {
  if (records.isEmpty) return;
  
  final recentRecords = records.take(7).toList();
  final avgDuration = recentRecords
    .map((r) => r.duration)
    .reduce((a, b) => a + b) / recentRecords.length;
  
  final trend = _analyzeTrend(recentRecords);
  
  statistics.value = SleepStatistics(
    averageDuration: avgDuration,
    averageEfficiency: 85.0,
    totalRecords: recentRecords.length,
    trend: trend,
  );
}

String _analyzeTrend(List<SleepRecord> records) {
  if (records.length < 3) return '数据不足';
  
  final mid = records.length ~/ 2;
  final firstAvg = records.sublist(0, mid)
    .map((r) => r.duration)
    .reduce((a, b) => a + b) / mid;
  final secondAvg = records.sublist(mid)
    .map((r) => r.duration)
    .reduce((a, b) => a + b) / (records.length - mid);
  
  final diff = secondAvg - firstAvg;
  if (diff > 30) return '改善中';
  if (diff < -30) return '下降';
  return '稳定';
}

统计计算分析最近7天的睡眠数据。趋势分析比较前后半段平均值,30分钟为阈值。

📈 简化处理:用简单前后对比代替复杂时间序列分析,既能反映变化又不过于敏感。

用户界面设计

创建睡眠监测主界面:

class SleepView extends GetView<SleepController> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFF5F7FA),
      appBar: AppBar(
        title: Text('睡眠监测', style: TextStyle(fontSize: 22.sp)),
        backgroundColor: Color(0xFF6B5CE7),
        actions: [
          IconButton(
            icon: Icon(Icons.add_circle, size: 32.sp),
            onPressed: _showAddDialog,
          ),
        ],
      ),
      body: RefreshIndicator(
        onRefresh: controller.loadSleepData,
        child: _buildBody(),
      ),
    );
  }
}

主界面使用紫色主题,因为紫色在色彩心理学中代表宁静和放松,与睡眠主题契合。

🎨 色彩选择的心理学考虑:我们选择紫色而不是蓝色,是因为紫色更柔和,不会像蓝色那样刺激。

实现睡眠统计卡片:

Widget _buildStatisticsCard() {
  return Obx(() {
    final stats = controller.statistics.value;
    if (stats == null) return SizedBox.shrink();
    
    return Container(
      margin: EdgeInsets.all(16.w),
      padding: EdgeInsets.all(20.w),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [Color(0xFF6B5CE7), Color(0xFF8B7CE8)],
        ),
        borderRadius: BorderRadius.circular(16.r),
      ),
      child: Column(
        children: [
          Row(
            children: [
              Icon(Icons.bedtime, color: Colors.white, size: 28.sp),
              SizedBox(width: 12.w),
              Text('睡眠概况', style: TextStyle(
                fontSize: 20.sp,
                color: Colors.white,
                fontWeight: FontWeight.bold,
              )),
            ],
          ),
          SizedBox(height: 20.h),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              _buildStatItem(
                '平均时长',
                '${(stats.averageDuration / 60).toStringAsFixed(1)}小时',
                Icons.access_time,
              ),
              _buildStatItem(
                '睡眠效率',
                '${stats.averageEfficiency.toStringAsFixed(0)}%',
                Icons.trending_up,
              ),
              _buildStatItem(
                '趋势',
                stats.trend,
                Icons.show_chart,
              ),
            ],
          ),
        ],
      ),
    );
  });
}

统计卡片用渐变紫色背景营造舒适的视觉效果。三个关键指标并排显示,让用户一眼就能看到睡眠状况。

📊 数据展示的简化原则:我们只展示最重要的三个指标,避免信息过载。每个指标都配有图标,即使不识字也能大概理解含义。

设计睡眠记录卡片:

Widget _buildRecordCard(SleepRecord record) {
  final qualityColor = _getQualityColor(record.quality);
  
  return Card(
    margin: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
    child: Padding(
      padding: EdgeInsets.all(16.w),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Container(
                padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
                decoration: BoxDecoration(
                  color: qualityColor.withOpacity(0.1),
                  borderRadius: BorderRadius.circular(12.r),
                ),
                child: Text(record.quality, style: TextStyle(
                  color: qualityColor,
                  fontWeight: FontWeight.bold,
                )),
              ),
              Spacer(),
              Text(_formatDate(record.bedTime), style: TextStyle(
                fontSize: 14.sp,
                color: Colors.grey[600],
              )),
            ],
          ),
          SizedBox(height: 12.h),
          Row(
            children: [
              Icon(Icons.bedtime, size: 20.sp, color: Colors.grey[600]),
              SizedBox(width: 8.w),
              Text('${_formatTime(record.bedTime)} - ${_formatTime(record.wakeTime)}',
                style: TextStyle(fontSize: 16.sp)),
            ],
          ),
          SizedBox(height: 8.h),
          Row(
            children: [
              _buildInfoChip('睡眠 ${(record.duration / 60).toStringAsFixed(1)}小时'),
              SizedBox(width: 8.w),
              _buildInfoChip('醒来 ${record.awakeCount}次'),
            ],
          ),
          if (record.note != null)
            Padding(
              padding: EdgeInsets.only(top: 8.h),
              child: Text(record.note!, style: TextStyle(
                fontSize: 14.sp,
                color: Colors.grey[600],
                fontStyle: FontStyle.italic,
              )),
            ),
        ],
      ),
    ),
  );
}

每条睡眠记录用卡片展示,睡眠质量用醒目的颜色标签显示。时间范围用横杠连接,清晰表达睡眠的起止时间。

🎨 信息层次的视觉设计:最重要的睡眠质量放在最上方,用彩色标签突出。时间信息用图标辅助,数据信息用芯片样式展示,形成清晰的视觉层次。

添加睡眠记录功能

实现添加记录对话框:

void _showAddDialog() {
  DateTime? selectedBedTime;
  DateTime? selectedWakeTime;
  int awakeCount = 0;
  final noteController = TextEditingController();
  
  Get.dialog(
    AlertDialog(
      title: Text('记录睡眠'),
      content: SingleChildScrollView(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ListTile(
              leading: Icon(Icons.bedtime),
              title: Text('上床时间'),
              subtitle: Text(selectedBedTime == null 
                ? '点击选择' 
                : _formatDateTime(selectedBedTime!)),
              onTap: () async {
                final time = await _selectDateTime(Get.context!);
                if (time != null) selectedBedTime = time;
              },
            ),
            ListTile(
              leading: Icon(Icons.wb_sunny),
              title: Text('起床时间'),
              subtitle: Text(selectedWakeTime == null 
                ? '点击选择' 
                : _formatDateTime(selectedWakeTime!)),
              onTap: () async {
                final time = await _selectDateTime(Get.context!);
                if (time != null) selectedWakeTime = time;
              },
            ),
            TextField(
              decoration: InputDecoration(
                labelText: '夜间醒来次数',
                hintText: '输入数字',
              ),
              keyboardType: TextInputType.number,
              onChanged: (value) => awakeCount = int.tryParse(value) ?? 0,
            ),
            SizedBox(height: 16.h),
            TextField(
              controller: noteController,
              decoration: InputDecoration(
                labelText: '备注(可选)',
                hintText: '如:睡前喝了牛奶',
              ),
              maxLines: 2,
            ),
          ],
        ),
      ),
      actions: [
        TextButton(
          onPressed: () => Get.back(),
          child: Text('取消'),
        ),
        ElevatedButton(
          onPressed: () {
            if (selectedBedTime != null && selectedWakeTime != null) {
              controller.addSleepRecord(
                bedTime: selectedBedTime!,
                wakeTime: selectedWakeTime!,
                awakeCount: awakeCount,
                note: noteController.text.isEmpty ? null : noteController.text,
              );
              Get.back();
            } else {
              Get.snackbar('提示', '请选择上床和起床时间');
            }
          },
          child: Text('保存'),
        ),
      ],
    ),
  );
}

添加对话框使用列表项选择时间,比直接弹出时间选择器更友好。醒来次数用数字输入,备注提供了示例文字引导用户。

📝 输入方式的适老化设计:我们用点击列表项的方式选择时间,而不是复杂的滚轮选择器。这样老年用户更容易操作,不会因为手抖而选错时间。

睡眠建议功能

实现个性化睡眠建议:

List<String> generateSleepAdvice(SleepRecord record) {
  final advice = <String>[];
  
  if (record.duration < 360) {
    advice.add('睡眠时间不足6小时,建议提前30分钟上床');
  }
  
  if (record.awakeCount > 2) {
    advice.add('夜间醒来${record.awakeCount}次,睡前避免喝太多水');
  }
  
  if (record.sleepEfficiency < 75) {
    advice.add('睡眠效率较低,建议睡前1小时不看手机');
  }
  
  if (advice.isEmpty) {
    advice.add('睡眠质量很好,继续保持规律作息');
  }
  
  return advice;
}

建议系统根据实际睡眠数据生成。睡眠时间不足建议提前上床,频繁醒来建议控制水分摄入,效率低建议减少电子设备使用。

💡 建议的实用性:所有建议都是老年人容易执行的,比如"提前30分钟上床"比"调整生物钟"更具体可行。

数据存储实现

class SleepStorage {
  static const String _key = 'sleep_records';
  
  static Future<void> saveRecords(List<SleepRecord> records) async {
    final prefs = await SharedPreferences.getInstance();
    final jsonList = records.map((r) => r.toJson()).toList();
    await prefs.setString(_key, json.encode(jsonList));
  }
  
  static Future<List<SleepRecord>> loadRecords() async {
    final prefs = await SharedPreferences.getInstance();
    final jsonString = prefs.getString(_key);
    if (jsonString == null) return [];
    
    final jsonList = json.decode(jsonString) as List;
    return jsonList.map((json) => SleepRecord.fromJson(json)).toList();
  }
}

使用本地存储保存睡眠数据,确保数据安全。即使应用关闭,用户的睡眠记录也不会丢失。

� 数据安全的重要性:睡眠数据是用户的隐私信息,我们采用本地存储而不是云端,保护用户隐私。

实际应用反馈

在社区试点使用后,我们收到了很多正面反馈。一位68岁的用户说:"现在每天早上起来就记录睡眠,看到’良好’两个字心里就踏实了。"另一位用户反馈:“发现自己睡前喝水太多,改掉这个习惯后,半夜醒来的次数明显减少了。”

这些反馈让我们认识到,简单的记录和反馈就能帮助老年用户改善睡眠质量。不需要复杂的算法,只需要让他们看到变化得到鼓励

通过这个睡眠监测功能的开发,我深刻体会到适老化设计的核心:不是功能越多越好,而是把最需要的功能做到极致简单

核心设计原则总结:

  • 简化数据模型:只记录最关键的睡眠指标
  • 直观的质量评价:用文字而不是数字表达睡眠质量
  • 实用的改善建议:提供具体可执行的生活指导
  • 温暖的视觉设计:用柔和的颜色营造舒适感
  • 本地数据存储:保护用户隐私安全

这个睡眠监测模块已经帮助了数百位老年用户改善睡眠质量。未来我们还将加入睡眠提醒、睡眠音乐等功能,让老年人睡得更好。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐