在这里插入图片描述

前言

运动提醒是帮助用户养成规律运动习惯的重要功能。通过智能的提醒机制,应用可以在合适的时间提醒用户进行运动,避免久坐带来的健康问题。本文将详细介绍如何在Flutter与OpenHarmony平台上实现完善的运动提醒组件,包括定时提醒、久坐提醒、智能提醒等功能模块的完整实现方案。

运动提醒的设计需要在有效性和不打扰之间取得平衡。过于频繁的提醒会让用户感到厌烦,过于稀少又起不到提醒作用。我们需要根据用户的作息习惯和运动目标,提供个性化的提醒策略,让提醒真正帮助用户建立健康的运动习惯。

Flutter提醒配置模型

class ReminderConfig {
  final String id;
  final ReminderType type;
  final TimeOfDay time;
  final List<int> weekdays;
  final bool enabled;
  final String message;
  final bool vibrate;
  final bool sound;
  
  ReminderConfig({
    required this.id,
    required this.type,
    required this.time,
    required this.weekdays,
    this.enabled = true,
    this.message = '该运动了!',
    this.vibrate = true,
    this.sound = true,
  });
  
  bool shouldTriggerOn(DateTime dateTime) {
    if (!enabled) return false;
    if (!weekdays.contains(dateTime.weekday)) return false;
    return dateTime.hour == time.hour && dateTime.minute == time.minute;
  }
}

enum ReminderType { scheduled, sedentary, goal }

提醒配置模型定义了提醒的完整属性。type区分三种提醒类型:定时提醒、久坐提醒和目标提醒。time指定提醒时间,weekdays指定生效的星期几,支持工作日、周末或自定义组合。enabled控制提醒开关,message自定义提醒文案,vibrate和sound控制振动和声音。shouldTriggerOn方法判断指定时间是否应该触发提醒,综合考虑启用状态、星期和时间三个条件。这种设计支持灵活的提醒规则配置。

OpenHarmony系统提醒服务

import reminderAgentManager from '@ohos.reminderAgentManager';

class SystemReminderService {
  async createScheduledReminder(hour: number, minute: number, weekdays: Array<number>, message: string): Promise<number> {
    let reminderRequest: reminderAgentManager.ReminderRequestAlarm = {
      reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_ALARM,
      hour: hour,
      minute: minute,
      daysOfWeek: weekdays,
      title: '运动提醒',
      content: message,
      ringDuration: 10,
      snoozeTimes: 2,
      snoozeInterval: 5,
      actionButton: [
        { title: '开始运动', type: reminderAgentManager.ActionButtonType.ACTION_BUTTON_TYPE_CUSTOM },
        { title: '稍后提醒', type: reminderAgentManager.ActionButtonType.ACTION_BUTTON_TYPE_SNOOZE },
      ],
    };
    
    let reminderId = await reminderAgentManager.publishReminder(reminderRequest);
    return reminderId;
  }
  
  async cancelReminder(reminderId: number): Promise<void> {
    await reminderAgentManager.cancelReminder(reminderId);
  }
  
  async cancelAllReminders(): Promise<void> {
    await reminderAgentManager.cancelAllReminders();
  }
}

系统提醒服务利用OpenHarmony的reminderAgentManager实现系统级提醒。createScheduledReminder方法创建定时闹钟提醒,设置触发时间、重复星期、提醒内容等参数。actionButton配置提醒弹窗的按钮,用户可以选择"开始运动"直接进入应用,或"稍后提醒"延迟提醒。snoozeTimes和snoozeInterval设置贪睡次数和间隔。系统级提醒的优势是即使应用未运行也能准时触发,确保用户不会错过提醒。cancelReminder和cancelAllReminders方法用于取消提醒。

Flutter提醒列表组件

class ReminderListView extends StatelessWidget {
  final List<ReminderConfig> reminders;
  final Function(String, bool) onToggle;
  final Function(String) onDelete;
  final Function(ReminderConfig) onEdit;
  
  const ReminderListView({
    Key? key,
    required this.reminders,
    required this.onToggle,
    required this.onDelete,
    required this.onEdit,
  }) : super(key: key);
  
  
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: reminders.length,
      itemBuilder: (context, index) {
        var reminder = reminders[index];
        return Dismissible(
          key: Key(reminder.id),
          direction: DismissDirection.endToStart,
          onDismissed: (_) => onDelete(reminder.id),
          background: Container(
            color: Colors.red,
            alignment: Alignment.centerRight,
            padding: EdgeInsets.only(right: 16),
            child: Icon(Icons.delete, color: Colors.white),
          ),
          child: ListTile(
            leading: Icon(_getTypeIcon(reminder.type), color: reminder.enabled ? Colors.blue : Colors.grey),
            title: Text(_formatTime(reminder.time)),
            subtitle: Text(_formatWeekdays(reminder.weekdays)),
            trailing: Switch(
              value: reminder.enabled,
              onChanged: (value) => onToggle(reminder.id, value),
            ),
            onTap: () => onEdit(reminder),
          ),
        );
      },
    );
  }
  
  IconData _getTypeIcon(ReminderType type) {
    switch (type) {
      case ReminderType.scheduled: return Icons.alarm;
      case ReminderType.sedentary: return Icons.event_seat;
      case ReminderType.goal: return Icons.flag;
    }
  }
  
  String _formatTime(TimeOfDay time) {
    return '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
  }
  
  String _formatWeekdays(List<int> weekdays) {
    if (weekdays.length == 7) return '每天';
    if (weekdays.length == 5 && !weekdays.contains(6) && !weekdays.contains(7)) return '工作日';
    List<String> names = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日'];
    return weekdays.map((d) => names[d]).join(' ');
  }
}

提醒列表组件展示用户设置的所有提醒。每个列表项显示提醒类型图标、时间和重复规则,右侧的开关控制提醒启用状态。Dismissible组件支持左滑删除,红色背景和删除图标提示用户这是删除操作。点击列表项可以编辑提醒详情。_formatWeekdays方法智能格式化星期显示,如果是每天则显示"每天",如果是周一到周五则显示"工作日",否则列出具体的星期几。这种设计让提醒管理直观高效。

OpenHarmony久坐检测服务

import sensor from '@ohos.sensor';

class SedentaryDetectionService {
  private lastActivityTime: number = Date.now();
  private sedentaryThreshold: number = 60 * 60 * 1000; // 1小时
  private isMonitoring: boolean = false;
  private onSedentaryCallback: (() => void) | null = null;
  
  startMonitoring(onSedentary: () => void): void {
    this.isMonitoring = true;
    this.onSedentaryCallback = onSedentary;
    this.lastActivityTime = Date.now();
    
    sensor.on(sensor.SensorId.ACCELEROMETER, (data) => {
      let magnitude = Math.sqrt(data.x * data.x + data.y * data.y + data.z * data.z);
      if (magnitude > 12) {
        this.lastActivityTime = Date.now();
      }
    }, { interval: 5000000000 });
    
    setInterval(() => {
      this.checkSedentary();
    }, 60000);
  }
  
  private checkSedentary(): void {
    if (!this.isMonitoring) return;
    
    let idleTime = Date.now() - this.lastActivityTime;
    if (idleTime >= this.sedentaryThreshold && this.onSedentaryCallback) {
      this.onSedentaryCallback();
      this.lastActivityTime = Date.now();
    }
  }
  
  stopMonitoring(): void {
    this.isMonitoring = false;
    sensor.off(sensor.SensorId.ACCELEROMETER);
  }
}

久坐检测服务通过加速度传感器监测用户的活动状态。我们记录最后一次检测到活动的时间,当静止时间超过阈值(默认1小时)时触发久坐提醒。加速度传感器每5秒采样一次,计算加速度矢量幅度,超过12(考虑重力加速度约9.8)表示有明显运动。checkSedentary方法每分钟检查一次久坐状态,触发提醒后重置计时器。这种检测方式能够有效识别用户是否长时间保持静止,提醒他们起身活动。

Flutter提醒编辑表单

class ReminderEditForm extends StatefulWidget {
  final ReminderConfig? existingReminder;
  final Function(ReminderConfig) onSave;
  
  const ReminderEditForm({Key? key, this.existingReminder, required this.onSave}) : super(key: key);
  
  
  State<ReminderEditForm> createState() => _ReminderEditFormState();
}

class _ReminderEditFormState extends State<ReminderEditForm> {
  late TimeOfDay _time;
  late List<int> _weekdays;
  late String _message;
  late bool _vibrate;
  late bool _sound;
  
  
  void initState() {
    super.initState();
    var r = widget.existingReminder;
    _time = r?.time ?? TimeOfDay(hour: 8, minute: 0);
    _weekdays = r?.weekdays ?? [1, 2, 3, 4, 5];
    _message = r?.message ?? '该运动了!';
    _vibrate = r?.vibrate ?? true;
    _sound = r?.sound ?? true;
  }
  
  
  Widget build(BuildContext context) {
    return ListView(
      padding: EdgeInsets.all(16),
      children: [
        ListTile(
          title: Text('提醒时间'),
          trailing: Text('${_time.hour.toString().padLeft(2, '0')}:${_time.minute.toString().padLeft(2, '0')}'),
          onTap: () async {
            var picked = await showTimePicker(context: context, initialTime: _time);
            if (picked != null) setState(() => _time = picked);
          },
        ),
        Divider(),
        Text('重复', style: TextStyle(fontWeight: FontWeight.bold)),
        Wrap(
          spacing: 8,
          children: List.generate(7, (index) {
            int day = index + 1;
            return FilterChip(
              label: Text(['一', '二', '三', '四', '五', '六', '日'][index]),
              selected: _weekdays.contains(day),
              onSelected: (selected) {
                setState(() {
                  if (selected) {
                    _weekdays.add(day);
                  } else {
                    _weekdays.remove(day);
                  }
                });
              },
            );
          }),
        ),
        SizedBox(height: 16),
        TextField(
          decoration: InputDecoration(labelText: '提醒内容', border: OutlineInputBorder()),
          controller: TextEditingController(text: _message),
          onChanged: (value) => _message = value,
        ),
        SwitchListTile(title: Text('振动'), value: _vibrate, onChanged: (v) => setState(() => _vibrate = v)),
        SwitchListTile(title: Text('声音'), value: _sound, onChanged: (v) => setState(() => _sound = v)),
      ],
    );
  }
}

提醒编辑表单提供完整的提醒配置界面。时间选择使用系统的TimePicker,点击后弹出时间选择器。星期选择使用FilterChip组件,支持多选,选中状态通过颜色区分。提醒内容使用TextField输入,用户可以自定义提醒文案。振动和声音使用SwitchListTile控制。表单支持新建和编辑两种模式,编辑模式下会加载已有提醒的配置。这种表单设计覆盖了提醒的所有可配置项,操作直观便捷。

OpenHarmony提醒存储服务

import dataPreferences from '@ohos.data.preferences';

class ReminderStorageService {
  private preferences: dataPreferences.Preferences | null = null;
  
  async initialize(context: Context): Promise<void> {
    this.preferences = await dataPreferences.getPreferences(context, 'reminders');
  }
  
  async saveReminder(reminder: object): Promise<void> {
    if (!this.preferences) return;
    
    let reminders = await this.getAllReminders();
    let index = reminders.findIndex((r: object) => r['id'] === reminder['id']);
    
    if (index >= 0) {
      reminders[index] = reminder;
    } else {
      reminders.push(reminder);
    }
    
    await this.preferences.put('reminders', JSON.stringify(reminders));
    await this.preferences.flush();
  }
  
  async getAllReminders(): Promise<Array<object>> {
    if (!this.preferences) return [];
    let json = await this.preferences.get('reminders', '[]') as string;
    return JSON.parse(json);
  }
  
  async deleteReminder(id: string): Promise<void> {
    if (!this.preferences) return;
    
    let reminders = await this.getAllReminders();
    reminders = reminders.filter((r: object) => r['id'] !== id);
    
    await this.preferences.put('reminders', JSON.stringify(reminders));
    await this.preferences.flush();
  }
}

提醒存储服务管理提醒配置的持久化。saveReminder方法保存或更新提醒,通过id判断是新建还是更新。getAllReminders方法获取所有提醒配置,返回数组格式便于遍历。deleteReminder方法删除指定id的提醒。数据以JSON字符串形式存储在偏好设置中,这种方式简单高效,适合存储结构化的配置数据。每次修改后调用flush确保数据持久化,防止应用异常退出导致数据丢失。

Flutter智能提醒组件

class SmartReminderService {
  static TimeOfDay suggestBestTime(List<DateTime> workoutHistory) {
    if (workoutHistory.isEmpty) {
      return TimeOfDay(hour: 18, minute: 0);
    }
    
    Map<int, int> hourCounts = {};
    for (var workout in workoutHistory) {
      int hour = workout.hour;
      hourCounts[hour] = (hourCounts[hour] ?? 0) + 1;
    }
    
    int bestHour = hourCounts.entries
        .reduce((a, b) => a.value > b.value ? a : b)
        .key;
    
    return TimeOfDay(hour: bestHour, minute: 0);
  }
  
  static List<int> suggestBestWeekdays(List<DateTime> workoutHistory) {
    if (workoutHistory.isEmpty) {
      return [1, 2, 3, 4, 5];
    }
    
    Map<int, int> dayCounts = {};
    for (var workout in workoutHistory) {
      int day = workout.weekday;
      dayCounts[day] = (dayCounts[day] ?? 0) + 1;
    }
    
    var sortedDays = dayCounts.entries.toList()
      ..sort((a, b) => b.value.compareTo(a.value));
    
    return sortedDays.take(5).map((e) => e.key).toList();
  }
  
  static String generateSmartMessage(String userName, int streak) {
    if (streak >= 7) {
      return '$userName,您已连续运动$streak天,继续保持!';
    } else if (streak >= 3) {
      return '$userName,连续$streak天运动了,今天也要加油哦!';
    } else {
      return '$userName,该运动了,让身体动起来吧!';
    }
  }
}

智能提醒服务根据用户的运动历史提供个性化建议。suggestBestTime方法分析用户过去运动的时间分布,找出最常运动的小时作为推荐提醒时间。suggestBestWeekdays方法分析运动的星期分布,推荐运动频率最高的几天。generateSmartMessage方法根据用户的连续运动天数生成个性化的提醒文案,连续运动越多天,文案越鼓励。这种智能化的提醒策略比固定的提醒更能契合用户的习惯,提高提醒的有效性。

Flutter提醒通知组件

class ReminderNotificationCard extends StatelessWidget {
  final String message;
  final VoidCallback onStart;
  final VoidCallback onSnooze;
  final VoidCallback onDismiss;
  
  const ReminderNotificationCard({
    Key? key,
    required this.message,
    required this.onStart,
    required this.onSnooze,
    required this.onDismiss,
  }) : super(key: key);
  
  
  Widget build(BuildContext context) {
    return Card(
      elevation: 8,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: EdgeInsets.all(20),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(Icons.notifications_active, size: 48, color: Colors.orange),
            SizedBox(height: 16),
            Text('运动提醒', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
            SizedBox(height: 8),
            Text(message, textAlign: TextAlign.center, style: TextStyle(color: Colors.grey[600])),
            SizedBox(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                TextButton(onPressed: onDismiss, child: Text('忽略')),
                TextButton(onPressed: onSnooze, child: Text('稍后提醒')),
                ElevatedButton(onPressed: onStart, child: Text('开始运动')),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

提醒通知卡片在应用内展示提醒内容。卡片顶部显示铃铛图标和标题,中间显示提醒消息,底部提供三个操作按钮:忽略、稍后提醒和开始运动。"开始运动"使用ElevatedButton突出显示,引导用户采取积极行动。卡片使用较大的elevation产生明显的阴影效果,在页面上突出显示。这种设计在用户打开应用时展示待处理的提醒,让用户可以快速响应或处理提醒。

总结

本文全面介绍了Flutter与OpenHarmony平台上运动提醒组件的实现方案。从提醒配置到系统提醒,从久坐检测到智能建议,从提醒管理到通知展示,涵盖了提醒功能的各个方面。通过灵活的配置选项和智能的提醒策略,我们可以帮助用户建立规律的运动习惯,在合适的时间提醒他们进行运动,促进身体健康。欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

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

更多推荐