import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:timezone/data/latest_all.dart' as tz; import 'package:timezone/timezone.dart' as tz; class NotificationService { static final NotificationService _instance = NotificationService._internal(); factory NotificationService() => _instance; NotificationService._internal(); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); Future init() async { tz.initializeTimeZones(); // MUST do this final String timeZoneName = await tz.local.name; print("Device Timezone: $timeZoneName"); const AndroidInitializationSettings androidInit = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initSettings = InitializationSettings(android: androidInit); await flutterLocalNotificationsPlugin.initialize( initSettings, onDidReceiveNotificationResponse: (details) { print("Notification clicked!"); }, ); } Future scheduleDailyNotification({ required int id, required int hour, required int minute, String title = 'Check what’s new 👀', String body = 'Open the app to explore exciting updates!', }) async { final now = tz.TZDateTime.now(tz.local); // Correctly calculate the next scheduled time tz.TZDateTime scheduledDate = tz.TZDateTime(tz.local, now.year, now.month, now.day, hour, minute); if (scheduledDate.isBefore(now)) { // If time has already passed, schedule for tomorrow scheduledDate = scheduledDate.add(const Duration(days: 1)); } final androidDetails = AndroidNotificationDetails( 'daily_channel', 'Daily Notifications', importance: Importance.max, priority: Priority.max, ); final notificationDetails = NotificationDetails(android: androidDetails); await flutterLocalNotificationsPlugin.zonedSchedule( id, title, body, scheduledDate, notificationDetails, androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, matchDateTimeComponents: DateTimeComponents.time, ); print("Scheduled notification at $hour:$minute (Next trigger: $scheduledDate)"); } }