在这里插入图片描述

开篇

良好的口腔护理习惯需要持之以恒,而提醒功能正是帮助用户养成习惯的得力助手。通过设置定时提醒,用户可以在合适的时间收到刷牙、用药、检查等提示,从而更好地维护口腔健康。

本文将介绍如何在 Flutter 中实现一个功能丰富的提醒设置页面,涵盖刷牙提醒、用药提醒、检查提醒等多种类型的提醒管理。

页面功能规划

提醒设置页面需要支持以下功能模块:

  • 刷牙提醒:早中晚三个时段的刷牙提醒开关
  • 用药提醒:展示用户设置的用药提醒列表
  • 检查提醒:定期检查和洗牙的提醒设置
  • 自定义提醒:支持用户添加个性化的提醒

页面结构实现

提醒设置页面采用 StatelessWidget,数据通过 ConsumerAppProvider 获取:

class ReminderPage extends StatelessWidget {
  const ReminderPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('提醒设置')),
      body: Consumer<AppProvider>(
        builder: (context, provider, _) {
          return SingleChildScrollView(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [

使用 SingleChildScrollView 确保内容较多时可以滚动查看,crossAxisAlignment 设为 start 让标题左对齐。

刷牙提醒模块

刷牙提醒是最基础也是最重要的提醒类型,我们设置了早中晚三个时段:

                const Text('刷牙提醒', 
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                const SizedBox(height: 12),
                _buildReminderCard(
                  icon: Icons.wb_sunny,
                  title: '早晨刷牙',
                  time: '07:00',
                  isEnabled: true,
                ),
                _buildReminderCard(
                  icon: Icons.wb_cloudy,
                  title: '中午刷牙',
                  time: '12:30',
                  isEnabled: false,
                ),
                _buildReminderCard(
                  icon: Icons.nightlight,
                  title: '晚上刷牙',
                  time: '21:00',
                  isEnabled: true,
                ),

每个时段使用不同的图标来表示:太阳代表早晨,云朵代表中午,月亮代表晚上。这种视觉设计让用户一目了然。

提醒卡片组件

提醒卡片是一个可复用的组件,接收图标、标题、时间和启用状态作为参数:

Widget _buildReminderCard({
  required IconData icon,
  required String title,
  required String time,
  required bool isEnabled,
}) {
  return Container(
    margin: const EdgeInsets.only(bottom: 12),
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12),
    ),

卡片使用白色背景和圆角设计,与应用整体风格保持一致。底部留有间距,让多个卡片之间有适当的视觉分隔。

卡片内部布局采用 Row 实现水平排列:

    child: Row(
      children: [
        Container(
          padding: const EdgeInsets.all(10),
          decoration: BoxDecoration(
            color: const Color(0xFF26A69A).withOpacity(0.1),
            shape: BoxShape.circle,
          ),
          child: Icon(icon, color: const Color(0xFF26A69A)),
        ),
        const SizedBox(width: 16),

图标放在圆形容器中,使用主题色的浅色版本作为背景,营造出柔和的视觉效果。

标题和时间垂直排列:

        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
              Text(time, style: TextStyle(color: Colors.grey.shade600)),
            ],
          ),
        ),

使用 Expanded 让文字区域占据剩余空间,标题加粗显示,时间使用灰色次要文字。

开关控件放在最右侧:

        Switch(
          value: isEnabled,
          onChanged: (value) {},
          activeColor: const Color(0xFF26A69A),
        ),
      ],
    ),
  );
}

Switch 组件的激活颜色设为主题色,与整体设计协调。目前 onChanged 为空实现,实际项目中需要连接到状态管理。

用药提醒模块

用药提醒展示用户设置的药物提醒列表:

                const SizedBox(height: 24),
                const Text('用药提醒', 
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                const SizedBox(height: 12),
                ...provider.medicineReminders.map(
                    (reminder) => _buildMedicineCard(reminder)),

使用展开运算符将提醒列表映射为卡片组件。数据来自 AppProvider 中的 medicineReminders 列表。

用药提醒卡片的实现:

Widget _buildMedicineCard(dynamic reminder) {
  return Container(
    margin: const EdgeInsets.only(bottom: 12),
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12),
    ),
    child: Row(
      children: [
        Container(
          padding: const EdgeInsets.all(10),
          decoration: BoxDecoration(
            color: Colors.orange.withOpacity(0.1),
            shape: BoxShape.circle,
          ),
          child: const Icon(Icons.medication, color: Colors.orange),
        ),

用药提醒使用橙色作为主色调,与刷牙提醒的绿色形成区分,帮助用户快速识别不同类型的提醒。

药物信息的展示:

        const SizedBox(width: 16),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(reminder.name, 
                   style: const TextStyle(fontWeight: FontWeight.bold)),
              Text('${reminder.dosage} · ${reminder.times.join(", ")}', 
                   style: TextStyle(color: Colors.grey.shade600)),
            ],
          ),
        ),
        Switch(
          value: reminder.isActive,
          onChanged: (value) {},
          activeColor: const Color(0xFF26A69A),
        ),
      ],
    ),
  );
}

药物名称作为标题,剂量和服用时间作为副标题。使用 join 方法将时间数组转换为逗号分隔的字符串。

检查提醒模块

定期的口腔检查和洗牙对于预防口腔疾病非常重要:

                const SizedBox(height: 24),
                const Text('检查提醒', 
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                const SizedBox(height: 12),
                _buildReminderCard(
                  icon: Icons.medical_services,
                  title: '定期检查',
                  time: '每6个月',
                  isEnabled: true,
                ),
                _buildReminderCard(
                  icon: Icons.cleaning_services,
                  title: '洗牙提醒',
                  time: '每6个月',
                  isEnabled: true,
                ),

检查提醒复用了刷牙提醒的卡片组件,只是图标和内容不同。这种组件复用的方式让代码更加简洁。

添加自定义提醒

页面底部提供了添加自定义提醒的按钮:

                const SizedBox(height: 24),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton.icon(
                    onPressed: () => _showAddReminderDialog(context),
                    icon: const Icon(Icons.add),
                    label: const Text('添加自定义提醒'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: const Color(0xFF26A69A),
                      foregroundColor: Colors.white,
                      padding: const EdgeInsets.symmetric(vertical: 14),
                    ),
                  ),
                ),
              ],
            ),
          );
        },
      ),
    );
  }

按钮使用 ElevatedButton.icon 组合图标和文字,宽度设为 double.infinity 占满整行,增加点击区域。

添加提醒对话框

点击添加按钮后弹出选择对话框:

void _showAddReminderDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (ctx) => AlertDialog(
      title: const Text('添加提醒'),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          ListTile(
            leading: const Icon(Icons.brush, color: Color(0xFF26A69A)),
            title: const Text('刷牙提醒'),
            onTap: () {
              Navigator.pop(ctx);
              ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('功能开发中')));
            },
          ),

对话框使用 Column 配合 mainAxisSize: MainAxisSize.min 让高度自适应内容。每个选项使用 ListTile 实现,包含图标、标题和点击事件。

其他提醒类型的选项:

          ListTile(
            leading: const Icon(Icons.medication, color: Colors.orange),
            title: const Text('用药提醒'),
            onTap: () {
              Navigator.pop(ctx);
              ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('功能开发中')));
            },
          ),
          ListTile(
            leading: const Icon(Icons.medical_services, color: Colors.blue),
            title: const Text('检查提醒'),
            onTap: () {
              Navigator.pop(ctx);
              ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('功能开发中')));
            },
          ),
        ],
      ),
    ),
  );
}

每种提醒类型使用不同的颜色图标,与页面中的卡片颜色保持一致。点击后关闭对话框并显示提示。

数据模型设计

用药提醒的数据模型定义:

class MedicineReminder {
  final String id;
  final String name;
  final String dosage;
  final List<String> times;
  final bool isActive;

  MedicineReminder({
    String? id,
    required this.name,
    required this.dosage,
    required this.times,
    this.isActive = true,
  }) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString();
}

模型包含药物名称、剂量、服用时间列表和激活状态。时间使用字符串列表存储,便于展示多个服用时间点。

Provider 数据管理

AppProvider 中管理提醒数据:

List<MedicineReminder> _medicineReminders = [];
List<MedicineReminder> get medicineReminders => _medicineReminders;

void addMedicineReminder(MedicineReminder reminder) {
  _medicineReminders.add(reminder);
  notifyListeners();
}

void toggleMedicineReminder(String id) {
  final index = _medicineReminders.indexWhere((r) => r.id == id);
  if (index != -1) {
    final old = _medicineReminders[index];
    _medicineReminders[index] = MedicineReminder(
      id: old.id,
      name: old.name,
      dosage: old.dosage,
      times: old.times,
      isActive: !old.isActive,
    );
    notifyListeners();
  }
}

提供添加提醒和切换提醒状态的方法,每次操作后通知界面更新。

测试数据初始化

为了开发调试,在 initTestData 中添加测试数据:

void initTestData() {
  _medicineReminders = [
    MedicineReminder(
      name: '漱口水',
      dosage: '10ml',
      times: ['08:00', '20:00'],
    ),
    MedicineReminder(
      name: '口腔喷雾',
      dosage: '2次',
      times: ['12:00'],
      isActive: false,
    ),
  ];
}

测试数据包含两条用药提醒,一条激活一条未激活,便于测试不同状态的显示效果。

本地通知集成思路

实际项目中,提醒功能需要集成本地通知。Flutter 中常用 flutter_local_notifications 插件:

// 初始化通知插件
final FlutterLocalNotificationsPlugin notifications = 
    FlutterLocalNotificationsPlugin();

Future<void> initNotifications() async {
  const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
  const iosSettings = DarwinInitializationSettings();
  const settings = InitializationSettings(
    android: androidSettings,
    iOS: iosSettings,
  );
  await notifications.initialize(settings);
}

这段代码展示了通知插件的初始化过程,需要分别配置 Android 和 iOS 的设置。

设置定时通知的示例:

Future<void> scheduleReminder(String title, TimeOfDay time) async {
  final now = DateTime.now();
  var scheduledDate = DateTime(
    now.year, now.month, now.day, 
    time.hour, time.minute
  );
  
  if (scheduledDate.isBefore(now)) {
    scheduledDate = scheduledDate.add(const Duration(days: 1));
  }

  await notifications.zonedSchedule(
    0,
    '口腔护理提醒',
    title,
    tz.TZDateTime.from(scheduledDate, tz.local),
    const NotificationDetails(
      android: AndroidNotificationDetails(
        'reminder_channel',
        '提醒通知',
        importance: Importance.high,
      ),
    ),
    androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
    uiLocalNotificationDateInterpretation:
        UILocalNotificationDateInterpretation.absoluteTime,
    matchDateTimeComponents: DateTimeComponents.time,
  );
}

matchDateTimeComponents: DateTimeComponents.time 设置让通知每天在指定时间重复触发。

Switch 组件状态绑定

将 Switch 组件与实际数据绑定:

Switch(
  value: isEnabled,
  onChanged: (value) {
    // 更新提醒状态
    provider.updateReminderStatus(reminderId, value);
  },
  activeColor: const Color(0xFF26A69A),
),

当用户切换开关时,调用 Provider 的方法更新状态,界面会自动响应变化。

时间选择器集成

添加提醒时需要选择提醒时间:

Future<TimeOfDay?> selectTime(BuildContext context) async {
  return showTimePicker(
    context: context,
    initialTime: TimeOfDay.now(),
    builder: (context, child) {
      return Theme(
        data: Theme.of(context).copyWith(
          colorScheme: const ColorScheme.light(
            primary: Color(0xFF26A69A),
          ),
        ),
        child: child!,
      );
    },
  );
}

通过 builder 参数自定义时间选择器的主题色,保持与应用风格一致。

用户体验细节

在设计提醒设置页面时,我们注意了以下用户体验细节:

分类清晰:将提醒按类型分组,每组有明确的标题,用户可以快速找到需要的设置。

视觉区分:不同类型的提醒使用不同的颜色图标,刷牙用绿色,用药用橙色,检查用蓝色。

操作便捷:开关控件放在卡片右侧,用户可以快速开启或关闭提醒,无需进入详情页。

反馈及时:操作后显示 SnackBar 提示,让用户知道操作结果。

扩展功能建议

基于当前实现,可以考虑添加以下功能:

  • 自定义时间:允许用户修改每个提醒的具体时间
  • 重复设置:支持设置每天、工作日、周末等不同的重复模式
  • 提醒铃声:允许用户选择不同的提醒铃声
  • 免打扰时段:设置特定时段内不发送提醒

总结

本文详细介绍了口腔护理 App 中提醒设置功能的实现。通过合理的分类设计和组件复用,我们构建了一个功能完善、易于使用的提醒管理页面。关键技术点包括:

  • 使用 Consumer 监听数据变化
  • 通过参数化组件实现卡片复用
  • 使用 Switch 组件实现开关控制
  • 通过对话框提供添加入口

提醒功能是帮助用户养成良好习惯的重要工具,希望本文的实现思路对你有所帮助。

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

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐