Flutter & OpenHarmony 运动App运动提醒组件开发
本文介绍了在Flutter和OpenHarmony平台实现运动提醒功能的完整方案。Flutter端通过ReminderConfig模型定义提醒配置,支持定时、久坐和目标三种提醒类型,并提供灵活的触发条件判断。OpenHarmony端利用系统提醒服务实现可靠的通知推送,即使应用未运行也能触发。Flutter的列表组件则提供了直观的提醒管理界面,支持编辑、删除和启用/禁用操作。该方案实现了跨平台的智能

前言
运动提醒是帮助用户养成规律运动习惯的重要功能。通过智能的提醒机制,应用可以在合适的时间提醒用户进行运动,避免久坐带来的健康问题。本文将详细介绍如何在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
更多推荐


所有评论(0)