Files
supplements/lib/services/notification_service.dart
Menno van Leeuwen f8c19f9051 initial commit
Signed-off-by: Menno van Leeuwen <menno@vleeuwen.me>
2025-08-26 01:21:26 +02:00

125 lines
4.1 KiB
Dart

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<void> 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<bool> requestPermissions() async {
final androidPlugin = _notifications.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
if (androidPlugin != null) {
await androidPlugin.requestNotificationsPermission();
}
final iosPlugin = _notifications.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>();
if (iosPlugin != null) {
await iosPlugin.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
return true;
}
Future<void> 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<void> 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<void> 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<void> 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,
);
}
}