#请添加图片描述

前言

睡眠质量直接影响身体健康和精神状态。记录睡眠需要三个关键信息:入睡时间、起床时间、睡眠质量。系统会自动计算睡眠时长,用户还可以主观评价睡眠质量。

这个页面的交互比较丰富,会用到时间选择器和表情评分。


状态变量

睡眠记录需要入睡时间、起床时间和质量评分三个状态。

class AddSleepPage extends StatefulWidget {
  const AddSleepPage({super.key});

  
  State<AddSleepPage> createState() => _AddSleepPageState();
}

class _AddSleepPageState extends State<AddSleepPage> {
  TimeOfDay _bedTime = const TimeOfDay(hour: 23, minute: 0);
  TimeOfDay _wakeTime = const TimeOfDay(hour: 7, minute: 0);
  int _quality = 3;

默认入睡时间 23:00,起床时间 7:00,睡眠时长 8 小时,是比较理想的作息。质量评分默认 3(良好)。


睡眠时长计算

根据入睡和起床时间自动计算睡眠时长。

  String get _duration {
    int mins = (_wakeTime.hour * 60 + _wakeTime.minute) - 
               (_bedTime.hour * 60 + _bedTime.minute);
    if (mins < 0) mins += 24 * 60;
    return '${mins ~/ 60}小时${mins % 60}分钟';
  }

这个 getter 会在每次状态变化时重新计算。如果起床时间"早于"入睡时间(比如 23:00 睡到 7:00),说明跨越了午夜,需要加上 24 小时。

~/ 是 Dart 的整除运算符,% 是取余。比如 480 分钟 = 8 小时 0 分钟。


睡眠时长卡片

用渐变背景的卡片突出显示睡眠时长。

  Widget _buildDurationCard() {
    return Container(
      width: double.infinity,
      padding: EdgeInsets.all(28.w),
      decoration: BoxDecoration(
        gradient: const LinearGradient(
          colors: [Color(0xFF845EC2), Color(0xFFA178DF)]
        ),
        borderRadius: BorderRadius.circular(24.r),
      ),
      child: Column(
        children: [
          Icon(Icons.nightlight_round, size: 40.w, color: Colors.white70),
          SizedBox(height: 12.h),
          Text('睡眠时长', style: TextStyle(
            fontSize: 13.sp, 
            color: Colors.white70
          )),
          SizedBox(height: 4.h),
          Text(_duration, style: TextStyle(
            fontSize: 32.sp, 
            fontWeight: FontWeight.w700, 
            color: Colors.white
          )),
        ],
      ),
    );
  }

紫色渐变是睡眠在整个App中的主题色,月亮图标强化了"睡眠"的概念。

睡眠时长会随着入睡/起床时间的修改实时更新,因为 _duration 是一个 getter,每次 build 都会重新计算。


时间选择卡片

入睡时间和起床时间用两个并排的卡片展示,点击可以修改。

  Widget _buildTimeCards() {
    return Row(
      children: [
        Expanded(child: _buildTimeCard('入睡时间', _bedTime, Icons.bedtime_outlined, () async {
          final t = await showTimePicker(context: context, initialTime: _bedTime);
          if (t != null) setState(() => _bedTime = t);
        })),
        SizedBox(width: 12.w),
        Expanded(child: _buildTimeCard('起床时间', _wakeTime, Icons.wb_sunny_outlined, () async {
          final t = await showTimePicker(context: context, initialTime: _wakeTime);
          if (t != null) setState(() => _wakeTime = t);
        })),
      ],
    );
  }

showTimePicker 是 Flutter 内置的时间选择器,会弹出一个圆盘式的时间选择界面。返回值是 Future<TimeOfDay?>,用户取消选择时返回 null。

两个卡片用 Expanded 平分宽度,中间留 12 像素间距。


单个时间卡片

抽取一个可复用的时间卡片组件。

  Widget _buildTimeCard(String label, TimeOfDay time, IconData icon, VoidCallback onTap) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        padding: EdgeInsets.all(16.w),
        decoration: BoxDecoration(
          color: Colors.white, 
          borderRadius: BorderRadius.circular(16.r)
        ),
        child: Column(
          children: [
            Icon(icon, size: 24.w, color: const Color(0xFF845EC2)),
            SizedBox(height: 8.h),
            Text(label, style: TextStyle(
              fontSize: 12.sp, 
              color: Colors.grey[500]
            )),
            SizedBox(height: 4.h),
            Text(time.format(context), style: TextStyle(
              fontSize: 20.sp, 
              fontWeight: FontWeight.w600, 
              color: const Color(0xFF1A1A2E)
            )),
          ],
        ),
      ),
    );
  }

入睡用床铺图标 bedtime_outlined,起床用太阳图标 wb_sunny_outlined,直观表达含义。

time.format(context) 会根据系统设置显示 12 小时制或 24 小时制。


睡眠质量评分

用表情符号让用户评价睡眠质量,比数字评分更直观。

  Widget _buildQualityCard() {
    final qualities = ['很差', '较差', '一般', '良好', '很好'];
    return Container(
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white, 
        borderRadius: BorderRadius.circular(16.r)
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('睡眠质量', style: TextStyle(
            fontSize: 14.sp, 
            fontWeight: FontWeight.w500, 
            color: const Color(0xFF1A1A2E)
          )),
          SizedBox(height: 16.h),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: List.generate(5, (i) {
              final isSelected = _quality == i;
              return GestureDetector(
                onTap: () => setState(() => _quality = i),
                child: Column(
                  children: [
                    Container(
                      width: 48.w,
                      height: 48.w,
                      decoration: BoxDecoration(
                        color: isSelected ? const Color(0xFF845EC2) : Colors.grey[100],
                        borderRadius: BorderRadius.circular(14.r),
                      ),
                      child: Center(
                        child: Text(
                          ['😫', '😕', '😐', '🙂', '😊'][i], 
                          style: TextStyle(fontSize: 22.sp)
                        )
                      ),
                    ),
                    SizedBox(height: 6.h),
                    Text(qualities[i], style: TextStyle(
                      fontSize: 11.sp, 
                      color: isSelected ? const Color(0xFF845EC2) : Colors.grey[500]
                    )),
                  ],
                ),
              );
            }),
          ),
        ],
      ),
    );
  }
}

5 个表情从左到右表示从差到好:😫😕😐🙂😊。选中的表情背景变成紫色,文字也变成紫色。

List.generate(5, (i) => ...) 生成 5 个评分选项,比手写 5 个 Widget 更简洁。


睡眠数据分析

有了入睡时间、起床时间和质量评分,可以做很多分析:

  • 平均睡眠时长
  • 入睡时间规律性
  • 睡眠质量趋势
  • 睡眠不足预警

这些功能可以在统计页面实现。


睡眠建议

根据睡眠数据给出建议:

String get _suggestion {
  final hours = (_wakeTime.hour * 60 + _wakeTime.minute - 
                 _bedTime.hour * 60 - _bedTime.minute + 24 * 60) % (24 * 60) / 60;
  
  if (hours < 6) return '睡眠时间不足,建议增加睡眠';
  if (hours > 9) return '睡眠时间过长,可能影响精神状态';
  if (_bedTime.hour >= 0 && _bedTime.hour < 22) return '入睡时间较早';
  if (_bedTime.hour >= 1 && _bedTime.hour < 6) return '熬夜对身体不好哦';
  return '作息规律,继续保持';
}

这种个性化建议能增加用户粘性。


小结

睡眠记录页面的特点:

  • 自动计算睡眠时长
  • 时间选择器修改入睡/起床时间
  • 表情评分评价睡眠质量
  • 紫色主题色,和App中其他睡眠相关内容一致

表情评分是一个很好的交互设计,比 1-5 分的数字评分更有趣,用户更愿意参与。

下一篇会讲添加运动记录页面,运动需要选择类型和时长,还会计算消耗的卡路里。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
请添加图片描述

Logo

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

更多推荐