You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 17:35:06