import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:timezone/timezone.dart' as tz; import 'package:timezone/data/latest.dart' as tz; import '../models/supplement.dart'; class NotificationService { static final NotificationService _instance = NotificationService._internal(); factory NotificationService() => _instance; NotificationService._internal(); final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin(); Future initialize() async { tz.initializeTimeZones(); const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); const DarwinInitializationSettings iosSettings = DarwinInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); const InitializationSettings initSettings = InitializationSettings( android: androidSettings, iOS: iosSettings, ); await _notifications.initialize(initSettings); } Future requestPermissions() async { final androidPlugin = _notifications.resolvePlatformSpecificImplementation(); if (androidPlugin != null) { await androidPlugin.requestNotificationsPermission(); } final iosPlugin = _notifications.resolvePlatformSpecificImplementation(); if (iosPlugin != null) { await iosPlugin.requestPermissions( alert: true, badge: true, sound: true, ); } return true; } Future scheduleSupplementReminders(Supplement supplement) async { // Cancel existing notifications for this supplement await cancelSupplementReminders(supplement.id!); for (int i = 0; i < supplement.reminderTimes.length; i++) { final timeStr = supplement.reminderTimes[i]; final timeParts = timeStr.split(':'); final hour = int.parse(timeParts[0]); final minute = int.parse(timeParts[1]); final notificationId = supplement.id! * 100 + i; // Unique ID for each reminder await _notifications.zonedSchedule( notificationId, 'Time for ${supplement.name}', 'Take ${supplement.dosage} ${supplement.unit}', _nextInstanceOfTime(hour, minute), const NotificationDetails( android: AndroidNotificationDetails( 'supplement_reminders', 'Supplement Reminders', channelDescription: 'Notifications for supplement intake reminders', importance: Importance.high, priority: Priority.high, ), iOS: DarwinNotificationDetails(), ), androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, matchDateTimeComponents: DateTimeComponents.time, ); } } Future cancelSupplementReminders(int supplementId) async { // Cancel all notifications for this supplement (up to 10 possible reminders) for (int i = 0; i < 10; i++) { final notificationId = supplementId * 100 + i; await _notifications.cancel(notificationId); } } Future cancelAllReminders() async { await _notifications.cancelAll(); } tz.TZDateTime _nextInstanceOfTime(int hour, int minute) { final tz.TZDateTime now = tz.TZDateTime.now(tz.local); tz.TZDateTime scheduledDate = tz.TZDateTime(tz.local, now.year, now.month, now.day, hour, minute); if (scheduledDate.isBefore(now)) { scheduledDate = scheduledDate.add(const Duration(days: 1)); } return scheduledDate; } Future showInstantNotification(String title, String body) async { const NotificationDetails notificationDetails = NotificationDetails( android: AndroidNotificationDetails( 'instant_notifications', 'Instant Notifications', channelDescription: 'Instant notifications for supplement app', importance: Importance.high, priority: Priority.high, ), iOS: DarwinNotificationDetails(), ); await _notifications.show( DateTime.now().millisecondsSinceEpoch ~/ 1000, title, body, notificationDetails, ); } }