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

Flutter本地定时通知仅Isyak时段生效问题求助

祈祷时间APP定时通知问题排查与解决

问题描述

参考YouTube视频开发祈祷时间APP,使用adhan.dart包获取祈祷时段,计划为5个祈祷时段配置本地定时通知,但实际仅Isyak时段的通知能正常触发。相关实现代码如下:

调度祈祷时间通知的包装类

class AzanWaktu{
  final bool isAzanOn;
  final String azanName;
  const AzanWaktu({
    this.azanName = "Omar & Hana  Azan  Istimewa Ramadan.mp3", this.isAzanOn = true});
}


class AzanReminders{
  AzanWaktu azanSubuh;
  AzanWaktu azanZohor;
  AzanWaktu azanAsar;
  AzanWaktu azanMaghrib;
  AzanWaktu azanIsyak;
  AzanReminders({
    this.azanSubuh = const AzanWaktu(),
    this.azanZohor = const AzanWaktu(),
    this.azanAsar = const AzanWaktu(),
    this.azanMaghrib =const AzanWaktu(),
    this.azanIsyak  = const AzanWaktu()});
  List<AzanReminder> azanReminders=[];

  initialization(){
    for(var i = 0; i<30; i++){
      // DateTime dateTime = DateTime.now().add(Duration(days:i));
      if(azanSubuh.isAzanOn){
        azanReminders.add(AzanReminder(
          id: 1 + 5 * i,azan:azanSubuh.azanName,time: prayerTimes.fajr,wkto:'Subuh'
        ));
      }
      if(azanZohor.isAzanOn){
        azanReminders.add(AzanReminder(
            id: 2 + 5 * i,azan:azanZohor.azanName,time: prayerTimes.dhuhr,wkto:'Zohor'
        ));
      }
      if(azanAsar.isAzanOn){
        azanReminders.add(AzanReminder(
            id: 3 + 5 * i,azan:azanAsar.azanName,time: prayerTimes.asr,wkto:'Asar'
        ));
      }
      if(azanMaghrib.isAzanOn){
        azanReminders.add(AzanReminder(
            id: 4 + 5 * i,azan:azanMaghrib.azanName,time: prayerTimes.maghrib,wkto:'Maghrib'
        ));
      }
      if(azanIsyak.isAzanOn){
        azanReminders.add(AzanReminder(
            id: 5 + 5 * i,azan:azanIsyak.azanName,time: prayerTimes.isha,wkto:'Isyak'
        ));
      }
    }
  }
}


class AzanReminder{
   int id;
   String azan;
   String wkto;
   DateTime time;
   AzanReminder({required this.id,required this.azan,required this.wkto,required this.time});

}

Notifications类实现

class Notifications {


  initialize(BuildContext context) async {
    void selectNotification(NotificationResponse notificationResponse) async {
      final String? payload = notificationResponse.payload;
      if (notificationResponse.payload != null) {
        debugPrint('notification payload: $payload');
      }
      await Navigator.push(
        context,
        MaterialPageRoute<void>(builder: (context) => bottomnavbar()),
      );
    }

    await flutterLocalNotificationsPlugin.initialize(initializationSettings,
        onDidReceiveNotificationResponse: selectNotification);
  }

  setNotification({required DateTime time ,required  int id,required String wkto, required String? azan}) async {
    tz.initializeTimeZones();
    Duration duration = time.difference(DateTime.now());
    if(!duration.isNegative) {
      await flutterLocalNotificationsPlugin.zonedSchedule(
          0,
          'Waktu Solat $wkto telah masuk ',
          'Ayuh Tunaikan Solat Sekarang',
          tz.TZDateTime.now(tz.local).add(duration),
          const NotificationDetails(
              android: AndroidNotificationDetails(
            'App',
            'Poket Solat App',
            channelDescription: 'Azan untuk Solat',
                priority: Priority.max,
                importance: Importance.high,
            playSound: true,

          )),
          androidAllowWhileIdle: true,
          uiLocalNotificationDateInterpretation:
              UILocalNotificationDateInterpretation.absoluteTime);
    }
  }

  cancelNotification(){
    flutterLocalNotificationsPlugin.cancelAll();
  }
}

通知初始化函数

void initState(){

    super.initState();
   isAzanGenerated = false;
    if (!isAzanGenerated) {
      AzanReminders azanReminders = AzanReminders(
        azanSubuh: AzanWaktu(isAzanOn: true),
        azanZohor: AzanWaktu(isAzanOn: true),
        azanAsar: AzanWaktu(
            isAzanOn: true, azanName: ""),
        azanMaghrib: AzanWaktu(
            isAzanOn: true, azanName: ""),
      );
      azanReminders.initialization();
      Notifications notifications = Notifications();
      notifications.initialize(context);
      notifications.cancelNotification();
      for (var azanReminder in azanReminders.azanReminders) {
        notifications.setNotification(
            time: azanReminder.time,
            id: azanReminder.id,
            wkto: azanReminder.wkto,
            azan: azanReminder.azan);
        isAzanGenerated = true;
      }
    }
  }

问题排查与解决

1. 通知ID重复覆盖

问题点:setNotification方法中调用zonedSchedule时硬编码了通知ID为0,所有祈祷时段的通知会共用同一个ID,后续调度的通知会覆盖前面的,最终只有最后一个Isyak通知生效。

解决方法:将传入的id参数替换硬编码的0:

await flutterLocalNotificationsPlugin.zonedSchedule(
    id, // 使用传入的唯一ID
    'Waktu Solat $wkto telah masuk ',
    'Ayuh Tunaikan Solat Sekarang',
    tz.TZDateTime.now(tz.local).add(duration),
    const NotificationDetails(
        android: AndroidNotificationDetails(
      'App',
      'Poket Solat App',
      channelDescription: 'Azan untuk Solat',
          priority: Priority.max,
          importance: Importance.high,
      playSound: true,

    )),
    androidAllowWhileIdle: true,
    uiLocalNotificationDateInterpretation:
        UILocalNotificationDateInterpretation.absoluteTime);

2. 祈祷时间未按日期递增

问题点:initialization循环30天,但始终使用同一个prayerTimes实例,没有根据循环天数计算对应日期的祈祷时间。如果当前时间已过当天的Subuh/Zohor/Asar/Maghrib时段,这些时段的duration会是负数,导致通知被跳过,只有未到的Isyak会被调度。

解决方法:在循环内生成对应日期的祈祷时间:

initialization(){
  for(var i = 0; i<30; i++){
    DateTime targetDate = DateTime.now().add(Duration(days:i));
    // 计算当天的祈祷时间(需替换为你的坐标和计算参数)
    PrayerTimes dailyPrayerTimes = PrayerTimes.calculate(
      coordinates: Coordinates(yourLatitude, yourLongitude),
      date: targetDate,
      calculationParameters: CalculationMethod.muslimWorldLeague(),
    );
    if(azanSubuh.isAzanOn){
      azanReminders.add(AzanReminder(
        id: 1 + 5 * i,
        azan:azanSubuh.azanName,
        time: dailyPrayerTimes.fajr,
        wkto:'Subuh'
      ));
    }
    if(azanZohor.isAzanOn){
      azanReminders.add(AzanReminder(
          id: 2 + 5 * i,
          azan:azanZohor.azanName,
          time: dailyPrayerTimes.dhuhr,
          wkto:'Zohor'
      ));
    }
    if(azanAsar.isAzanOn){
      azanReminders.add(AzanReminder(
          id: 3 + 5 * i,
          azan:azanAsar.azanName,
          time: dailyPrayerTimes.asr,
          wkto:'Asar'
      ));
    }
    if(azanMaghrib.isAzanOn){
      azanReminders.add(AzanReminder(
          id: 4 + 5 * i,
          azan:azanMaghrib.azanName,
          time: dailyPrayerTimes.maghrib,
          wkto:'Maghrib'
      ));
    }
    if(azanIsyak.isAzanOn){
      azanReminders.add(AzanReminder(
          id: 5 + 5 * i,
          azan:azanIsyak.azanName,
          time: dailyPrayerTimes.isha,
          wkto:'Isyak'
      ));
    }
  }
}

3. 时区初始化冗余调用

问题点:setNotification每次调用都执行tz.initializeTimeZones(),属于冗余操作,可能引发潜在问题。

解决方法:将时区初始化移到initialize方法中,仅执行一次:

initialize(BuildContext context) async {
  tz.initializeTimeZones(); // 移至此处
  void selectNotification(NotificationResponse notificationResponse) async {
    final String? payload = notificationResponse.payload;
    if (notificationResponse.payload != null) {
      debugPrint('notification payload: $payload');
    }
    await Navigator.push(
      context,
      MaterialPageRoute<void>(builder: (context) => bottomnavbar()),
    );
  }

  await flutterLocalNotificationsPlugin.initialize(initializationSettings,
      onDidReceiveNotificationResponse: selectNotification);
}

4. 状态标记时机错误

问题点:initState的循环中第一次迭代就将isAzanGenerated设为true,逻辑上应在所有通知调度完成后再标记。

解决方法:将赋值语句移到循环外部:

for (var azanReminder in azanReminders.azanReminders) {
  notifications.setNotification(
      time: azanReminder.time,
      id: azanReminder.id,
      wkto: azanReminder.wkto,
      azan: azanReminder.azan);
}
isAzanGenerated = true; // 循环结束后再标记

内容的提问来源于stack exchange,提问作者anon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 08:01:55