Flutter for OpenHarmony 身体健康状况记录App实战 - 提醒设置实现

#

前言

提醒设置页面让用户可以配置各种健康提醒,比如体重记录提醒、血压测量提醒、饮水提醒、睡眠提醒等。定时提醒能帮助用户养成良好的健康记录习惯。

这篇文章会讲解提醒设置页面的实现,包括提醒项列表和开关控制等核心功能。


页面整体结构

提醒设置页面使用 StatefulWidget,因为需要管理各个提醒的开关状态。

dart 复制代码
class ReminderSettingsPage extends StatefulWidget {
  const ReminderSettingsPage({super.key});

  @override
  State<ReminderSettingsPage> createState() => _ReminderSettingsPageState();
}

class _ReminderSettingsPageState extends State<ReminderSettingsPage> {
  bool _weightReminder = true;
  bool _bpReminder = true;
  bool _waterReminder = false;
  bool _sleepReminder = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFFAFAFC),
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        leading: IconButton(
          icon: Icon(Icons.arrow_back_ios_rounded, size: 20.w), 
          onPressed: () => Get.back()
        ),
        title: Text('提醒设置', style: TextStyle(fontSize: 17.sp, fontWeight: FontWeight.w600)),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(20.w),
        child: Container(
          decoration: BoxDecoration(
            color: Colors.white, 
            borderRadius: BorderRadius.circular(20.r)
          ),
          child: Column(
            children: [
              _buildReminderItem('体重记录', '每天 08:00', _weightReminder, (v) => setState(() => _weightReminder = v)),
              _buildDivider(),
              _buildReminderItem('血压测量', '每天 07:00', _bpReminder, (v) => setState(() => _bpReminder = v)),
              _buildDivider(),
              _buildReminderItem('饮水提醒', '每2小时', _waterReminder, (v) => setState(() => _waterReminder = v)),
              _buildDivider(),
              _buildReminderItem('睡眠提醒', '每天 22:30', _sleepReminder, (v) => setState(() => _sleepReminder = v)),
            ],
          ),
        ),
      ),
    );
  }
}

页面使用白色卡片包裹所有提醒项,提醒项之间用分隔线隔开。每个提醒项有独立的开关状态,通过 setState 更新。


提醒项组件

提醒项组件展示提醒名称、时间和开关控制。

dart 复制代码
Widget _buildReminderItem(String title, String time, bool value, Function(bool) onChanged) {
  return Padding(
    padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 14.h),
    child: Row(
      children: [
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(title, style: TextStyle(
                fontSize: 15.sp, 
                color: const Color(0xFF1A1A2E)
              )),
              SizedBox(height: 2.h),
              Text(time, style: TextStyle(
                fontSize: 12.sp, 
                color: Colors.grey[500]
              )),
            ],
          ),
        ),
        Switch(
          value: value, 
          onChanged: onChanged, 
          activeColor: const Color(0xFF6C63FF)
        ),
      ],
    ),
  );
}

Widget _buildDivider() {
  return Divider(
    height: 1, 
    indent: 16.w, 
    endIndent: 16.w, 
    color: Colors.grey[100]
  );
}

每个提醒项左边显示名称和时间,右边是开关。开关使用应用主题色 #6C63FF,和整体设计风格保持一致。

分隔线左右各留出 16.w 的边距,不会顶到边缘,视觉效果更好。


时间选择功能

点击提醒项可以修改提醒时间:

dart 复制代码
void _showTimePicker(BuildContext context, String title, TimeOfDay currentTime) async {
  final TimeOfDay? picked = await showTimePicker(
    context: context,
    initialTime: currentTime,
    builder: (context, child) {
      return Theme(
        data: Theme.of(context).copyWith(
          colorScheme: const ColorScheme.light(
            primary: Color(0xFF6C63FF),
            onPrimary: Colors.white,
            surface: Colors.white,
            onSurface: Color(0xFF1A1A2E),
          ),
        ),
        child: child!,
      );
    },
  );
  
  if (picked != null) {
    // 更新提醒时间
    final formatted = '${picked.hour.toString().padLeft(2, '0')}:${picked.minute.toString().padLeft(2, '0')}';
    // 保存新时间
  }
}

使用 Flutter 内置的 showTimePicker,通过 Theme 自定义颜色和应用主题保持一致。


提醒频率选择

有些提醒需要选择频率,比如饮水提醒:

dart 复制代码
void _showFrequencyPicker(BuildContext context) {
  final frequencies = ['每小时', '每2小时', '每3小时', '每4小时'];
  int selectedIndex = 1;
  
  showModalBottomSheet(
    context: context,
    backgroundColor: Colors.white,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.vertical(top: Radius.circular(20.r)),
    ),
    builder: (context) => StatefulBuilder(
      builder: (context, setState) => Container(
        padding: EdgeInsets.all(20.w),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text('选择提醒频率', style: TextStyle(
              fontSize: 16.sp,
              fontWeight: FontWeight.w600,
              color: const Color(0xFF1A1A2E),
            )),
            SizedBox(height: 20.h),
            ...frequencies.asMap().entries.map((entry) {
              final isSelected = entry.key == selectedIndex;
              return GestureDetector(
                onTap: () {
                  setState(() => selectedIndex = entry.key);
                },
                child: Container(
                  margin: EdgeInsets.only(bottom: 10.h),
                  padding: EdgeInsets.symmetric(vertical: 14.h),
                  decoration: BoxDecoration(
                    color: isSelected 
                      ? const Color(0xFF6C63FF).withOpacity(0.1) 
                      : Colors.grey[50],
                    borderRadius: BorderRadius.circular(12.r),
                    border: isSelected 
                      ? Border.all(color: const Color(0xFF6C63FF)) 
                      : null,
                  ),
                  child: Center(
                    child: Text(entry.value, style: TextStyle(
                      fontSize: 15.sp,
                      color: isSelected 
                        ? const Color(0xFF6C63FF) 
                        : const Color(0xFF1A1A2E),
                      fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400,
                    )),
                  ),
                ),
              );
            }),
            SizedBox(height: 10.h),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () {
                  Navigator.pop(context);
                  // 保存频率设置
                },
                style: ElevatedButton.styleFrom(
                  backgroundColor: const Color(0xFF6C63FF),
                  padding: EdgeInsets.symmetric(vertical: 14.h),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12.r),
                  ),
                ),
                child: Text('确定', style: TextStyle(
                  fontSize: 15.sp,
                  fontWeight: FontWeight.w600,
                  color: Colors.white,
                )),
              ),
            ),
          ],
        ),
      ),
    ),
  );
}

频率选择器用底部弹出菜单,选中的选项用紫色边框和背景高亮显示。


提醒开关状态保存

开关状态变化时需要保存到本地:

dart 复制代码
void _saveReminderState(String type, bool enabled) async {
  // 保存到 SharedPreferences
  // final prefs = await SharedPreferences.getInstance();
  // await prefs.setBool('reminder_$type', enabled);
  
  if (enabled) {
    // 注册本地通知
    _scheduleNotification(type);
  } else {
    // 取消本地通知
    _cancelNotification(type);
  }
}

void _scheduleNotification(String type) {
  // 根据类型设置不同的通知
  switch (type) {
    case 'weight':
      // 每天 08:00 提醒记录体重
      break;
    case 'bp':
      // 每天 07:00 提醒测量血压
      break;
    case 'water':
      // 每2小时提醒喝水
      break;
    case 'sleep':
      // 每天 22:30 提醒准备睡觉
      break;
  }
}

void _cancelNotification(String type) {
  // 取消对应类型的通知
}

开关状态保存到本地存储,同时注册或取消本地通知。


提醒详情设置

可以为每个提醒添加更详细的设置:

dart 复制代码
Widget _buildReminderDetailItem(
  String title, 
  String time, 
  String frequency,
  bool value, 
  Function(bool) onChanged,
  VoidCallback onTap,
) {
  return GestureDetector(
    onTap: onTap,
    child: Padding(
      padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 14.h),
      child: Row(
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(title, style: TextStyle(
                  fontSize: 15.sp,
                  color: const Color(0xFF1A1A2E),
                )),
                SizedBox(height: 4.h),
                Row(
                  children: [
                    Text(time, style: TextStyle(
                      fontSize: 12.sp,
                      color: Colors.grey[500],
                    )),
                    SizedBox(width: 8.w),
                    Container(
                      padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
                      decoration: BoxDecoration(
                        color: const Color(0xFF6C63FF).withOpacity(0.1),
                        borderRadius: BorderRadius.circular(4.r),
                      ),
                      child: Text(frequency, style: TextStyle(
                        fontSize: 10.sp,
                        color: const Color(0xFF6C63FF),
                      )),
                    ),
                  ],
                ),
              ],
            ),
          ),
          Switch(
            value: value,
            onChanged: onChanged,
            activeColor: const Color(0xFF6C63FF),
          ),
        ],
      ),
    ),
  );
}

详细设置项除了时间,还显示频率标签。点击整行可以进入详细设置页面。


提醒测试

可以添加一个测试按钮,让用户预览提醒效果:

dart 复制代码
Widget _buildTestButton() {
  return Container(
    margin: EdgeInsets.only(top: 20.h),
    child: TextButton(
      onPressed: () {
        // 发送测试通知
        _sendTestNotification();
      },
      child: Text('发送测试提醒', style: TextStyle(
        fontSize: 14.sp,
        color: const Color(0xFF6C63FF),
      )),
    ),
  );
}

void _sendTestNotification() {
  // 发送一条测试通知
  Get.snackbar(
    '测试提醒',
    '这是一条测试提醒消息',
    snackPosition: SnackPosition.TOP,
    backgroundColor: const Color(0xFF6C63FF),
    colorText: Colors.white,
    duration: const Duration(seconds: 3),
  );
}

测试按钮让用户可以预览提醒的效果,确认设置是否正确。


免打扰时段

可以设置免打扰时段,在这个时间段内不发送提醒:

dart 复制代码
Widget _buildDoNotDisturbSection() {
  return Container(
    margin: EdgeInsets.only(top: 20.h),
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(16.r),
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text('免打扰时段', style: TextStyle(
              fontSize: 15.sp,
              fontWeight: FontWeight.w500,
              color: const Color(0xFF1A1A2E),
            )),
            Switch(
              value: true,
              onChanged: (v) {},
              activeColor: const Color(0xFF6C63FF),
            ),
          ],
        ),
        SizedBox(height: 8.h),
        Text('23:00 - 07:00', style: TextStyle(
          fontSize: 13.sp,
          color: Colors.grey[500],
        )),
        SizedBox(height: 4.h),
        Text('在此时段内不会发送任何提醒', style: TextStyle(
          fontSize: 11.sp,
          color: Colors.grey[400],
        )),
      ],
    ),
  );
}

免打扰时段让用户可以在睡眠时间避免被打扰,提升使用体验。


小结

提醒设置页面通过开关控制和时间选择,让用户可以灵活配置各种健康提醒。

核心设计要点包括:提醒项使用统一的布局和开关样式,时间和频率选择使用底部弹出菜单,免打扰时段避免不必要的打扰。这些设计让用户能轻松管理自己的健康提醒,养成良好的记录习惯。


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

相关推荐
大雷神2 小时前
HarmonyOS智慧农业管理应用开发教程--高高种地-- 第19篇:语音合成 - TTS语音播报
华为·语音识别·harmonyos
2301_822365032 小时前
数据分析与科学计算
jvm·数据库·python
河北小博博2 小时前
分布式系统稳定性基石:熔断与限流的深度解析(附Python实战)
java·开发语言·python
黄连升2 小时前
Python学习第二天,系统学习基础
python·学习
西红市杰出青年2 小时前
CSS 选择器详细教程:原理、语法、方向/“轴”与实战
css·python
tudficdew2 小时前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
2601_949613022 小时前
flutter_for_openharmony家庭药箱管理app实战+药品详情实现
java·前端·flutter
爱学习的阿磊2 小时前
使用XGBoost赢得Kaggle比赛
jvm·数据库·python
智航GIS2 小时前
ArcGIS Python零基础脚本开发教程---1.1 Describe 函数
开发语言·python·arcgis