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
相关产品推荐
相关产品推荐

