Flutter药品APP:如何按用户设定时间发送重复本地通知?
Flutter药品APP重复本地通知调度方案
核心思路
基于用户输入的服药时间与频率,结合flutter_local_notifications插件生成对应重复规则的本地通知,同时通过SQflite存储服药计划,APP启动时重新调度所有通知以避免重启后丢失。
步骤1:依赖配置
添加必要的包到pubspec.yaml:
dependencies: flutter_local_notifications: ^16.1.0 sqflite: ^2.3.2 path_provider: ^2.1.1 flutter: sdk: flutter
步骤2:初始化通知插件
在APP启动时完成通知初始化:
final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin(); Future<void> initNotifications() async { // Android初始化配置 const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); // iOS初始化配置 const DarwinInitializationSettings iosSettings = DarwinInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); // 统一初始化设置 const InitializationSettings initSettings = InitializationSettings( android: androidSettings, iOS: iosSettings, ); await _notificationsPlugin.initialize(initSettings); }
步骤3:定义服药计划模型与SQflite适配
和数据库表结构对应,存储用户配置的服药信息:
class MedicationPlan { final int id; final String name; final String timeConfig; // 按频率存不同格式:如每日一次存"08:00",每日两次存"08:00,20:00",每周存"Monday 08:00" final String frequency; // 可选值:daily/twiceDaily/everyOtherDay/weekly MedicationPlan({ required this.id, required this.name, required this.timeConfig, required this.frequency, }); // 转数据库Map Map<String, dynamic> toMap() => { 'id': id, 'name': name, 'time_config': timeConfig, 'frequency': frequency, }; // 从数据库Map转模型 static MedicationPlan fromMap(Map<String, dynamic> map) => MedicationPlan( id: map['id'], name: map['name'], timeConfig: map['time_config'], frequency: map['frequency'], ); }
步骤4:根据频率调度重复通知
编写统一调度函数,针对不同频率生成对应规则的通知:
Future<void> scheduleMedicationAlert(MedicationPlan plan) async { // 通用通知配置 final AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'medication_alerts', '服药提醒', channelDescription: '按时服药的提醒通知', importance: Importance.max, priority: Priority.high, allowWhileIdle: true, // 锁屏/后台也能触发 ); final DarwinNotificationDetails iosDetails = DarwinNotificationDetails( sound: 'default', presentAlert: true, ); final NotificationDetails notificationDetails = NotificationDetails( android: androidDetails, iOS: iosDetails, ); switch (plan.frequency) { // 每日一次 case 'daily': final Time scheduledTime = _parseTime(plan.timeConfig); await _notificationsPlugin.showDailyAtTime( plan.id, '服药提醒', '该吃${plan.name}了', scheduledTime, notificationDetails, ); break; // 每日两次 case 'twiceDaily': final List<String> times = plan.timeConfig.split(','); for (int i = 0; i < times.length; i++) { final Time time = _parseTime(times[i]); await _notificationsPlugin.showDailyAtTime( plan.id + i, // 用不同ID区分两个通知 '服药提醒', '该吃${plan.name}了', time, notificationDetails, ); } break; // 隔日一次 case 'everyOtherDay': final Time scheduledTime = _parseTime(plan.timeConfig); if (Platform.isAndroid) { await _notificationsPlugin.periodicallyShow( plan.id, '服药提醒', '该吃${plan.name}了', RepeatInterval.everyTwoDays, notificationDetails, scheduleMode: ScheduleMode.exactAllowWhileIdle, ); } else if (Platform.isIOS) { DateTime firstTrigger = DateTime.now().copyWith( hour: scheduledTime.hour, minute: scheduledTime.minute, second: 0, ); if (firstTrigger.isBefore(DateTime.now())) { firstTrigger = firstTrigger.add(const Duration(days: 2)); } await _notificationsPlugin.zonedSchedule( plan.id, '服药提醒', '该吃${plan.name}了', firstTrigger, notificationDetails, androidAllowWhileIdle: true, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, repeatInterval: RepeatInterval.everyTwoDays, ); } break; // 每周一次 case 'weekly': final List<String> parts = plan.timeConfig.split(' '); final Weekday weekday = _parseWeekday(parts[0]); final Time time = _parseTime(parts[1]); await _notificationsPlugin.showWeeklyAtDayAndTime( plan.id, '服药提醒', '该吃${plan.name}了', weekday, time, notificationDetails, ); break; } } // 辅助函数:解析时间字符串为Time对象 Time _parseTime(String timeStr) { final List<String> split = timeStr.split(':'); return Time(int.parse(split[0]), int.parse(split[1])); } // 辅助函数:解析星期字符串为Weekday枚举 Weekday _parseWeekday(String weekdayStr) { switch (weekdayStr.toLowerCase()) { case 'monday': return Weekday.monday; case 'tuesday': return Weekday.tuesday; case 'wednesday': return Weekday.wednesday; case 'thursday': return Weekday.thursday; case 'friday': return Weekday.friday; case 'saturday': return Weekday.saturday; case 'sunday': return Weekday.sunday; default: return Weekday.monday; } }
步骤5:APP启动时重新调度所有通知
由于重启后本地通知会丢失,需从SQflite加载所有计划重新调度:
Future<void> rescheduleAllAlerts() async { final Database db = await _getDatabase(); // 替换为你的SQflite数据库实例获取逻辑 final List<Map<String, dynamic>> plansMap = await db.query('medication_plans'); final List<MedicationPlan> plans = plansMap.map(MedicationPlan.fromMap).toList(); for (final plan in plans) { await scheduleMedicationAlert(plan); } }
步骤6:处理通知取消
当用户删除服药计划时,需取消对应通知:
Future<void> cancelMedicationAlert(MedicationPlan plan) async { await _notificationsPlugin.cancel(plan.id); // 每日两次的情况需取消两个通知 if (plan.frequency == 'twiceDaily') { await _notificationsPlugin.cancel(plan.id + 1); } }
平台额外配置
- iOS:在
Info.plist中添加权限描述<key>NSAlertUsageDescription</key> <string>需要发送服药提醒通知</string> <key>NSBadgeUsageDescription</key> <string>需要更新应用徽章提醒服药</string> <key>NSSoundUsageDescription</key> <string>需要播放提示音提醒服药</string> - Android:在
AndroidManifest.xml中添加权限<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
内容的提问来源于stack exchange,提问作者Rachita Dongre
相关产品推荐
相关产品推荐

