147 lines
4.3 KiB
Dart
147 lines
4.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'package:timezone/timezone.dart' as tz;
|
|
import '../models/models.dart';
|
|
|
|
class NotificationService {
|
|
static final NotificationService _instance = NotificationService._internal();
|
|
factory NotificationService() => _instance;
|
|
NotificationService._internal();
|
|
|
|
final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin();
|
|
bool _initialized = false;
|
|
|
|
Future<void> initialize() async {
|
|
if (_initialized) return;
|
|
|
|
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
const iosSettings = DarwinInitializationSettings(
|
|
requestAlertPermission: true,
|
|
requestBadgePermission: true,
|
|
requestSoundPermission: true,
|
|
);
|
|
|
|
const initSettings = InitializationSettings(
|
|
android: androidSettings,
|
|
iOS: iosSettings,
|
|
);
|
|
|
|
await _notifications.initialize(
|
|
initSettings,
|
|
onDidReceiveNotificationResponse: _onNotificationTap,
|
|
);
|
|
|
|
_initialized = true;
|
|
}
|
|
|
|
void _onNotificationTap(NotificationResponse response) {
|
|
// Handle notification tap - mark routine as complete
|
|
final payload = response.payload;
|
|
if (payload != null) {
|
|
// Navigate to routine or mark complete
|
|
}
|
|
}
|
|
|
|
Future<void> scheduleRoutineReminder(Routine routine) async {
|
|
if (routine.reminders.isEmpty) return;
|
|
|
|
final scheduledTime = _parseTime(routine.schedule.time);
|
|
if (scheduledTime == null) return;
|
|
|
|
for (final reminder in routine.reminders) {
|
|
final reminderTime = scheduledTime.subtract(
|
|
Duration(minutes: reminder.minutesBefore),
|
|
);
|
|
|
|
await _notifications.zonedSchedule(
|
|
'${routine.id}_${reminder.id}'.hashCode,
|
|
'LifeFlow Reminder',
|
|
'Time for: ${routine.name}',
|
|
tz.TZDateTime.from(reminderTime, tz.local),
|
|
NotificationDetails(
|
|
android: AndroidNotificationDetails(
|
|
'routine_reminders',
|
|
'Routine Reminders',
|
|
channelDescription: 'Reminders for your daily routines',
|
|
importance: Importance.high,
|
|
priority: Priority.high,
|
|
showWhen: true,
|
|
enableVibration: true,
|
|
playSound: true,
|
|
actions: [
|
|
const AndroidNotificationAction(
|
|
'complete',
|
|
'Complete',
|
|
showsUserInterface: false,
|
|
),
|
|
const AndroidNotificationAction(
|
|
'snooze',
|
|
'Snooze 10min',
|
|
showsUserInterface: false,
|
|
),
|
|
],
|
|
),
|
|
iOS: const DarwinNotificationDetails(
|
|
presentAlert: true,
|
|
presentBadge: true,
|
|
presentSound: true,
|
|
),
|
|
),
|
|
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
|
|
uiLocalNotificationDateInterpretation:
|
|
UILocalNotificationDateInterpretation.absoluteTime,
|
|
payload: routine.id,
|
|
matchDateTimeComponents: DateTimeComponents.time,
|
|
);
|
|
}
|
|
}
|
|
|
|
DateTime? _parseTime(String timeStr) {
|
|
try {
|
|
final parts = timeStr.split(':');
|
|
if (parts.length != 2) return null;
|
|
|
|
final hour = int.parse(parts[0]);
|
|
final minute = int.parse(parts[1]);
|
|
|
|
final now = DateTime.now();
|
|
var scheduled = DateTime(now.year, now.month, now.day, hour, minute);
|
|
|
|
if (scheduled.isBefore(now)) {
|
|
scheduled = scheduled.add(const Duration(days: 1));
|
|
}
|
|
|
|
return scheduled;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> cancelRoutineReminders(String routineId) async {
|
|
// Cancel all notifications for this routine
|
|
// Note: In production, track notification IDs
|
|
}
|
|
|
|
Future<void> showImmediateNotification({
|
|
required String title,
|
|
required String body,
|
|
String? payload,
|
|
}) async {
|
|
await _notifications.show(
|
|
DateTime.now().millisecond,
|
|
title,
|
|
body,
|
|
const NotificationDetails(
|
|
android: AndroidNotificationDetails(
|
|
'immediate',
|
|
'Immediate Notifications',
|
|
importance: Importance.high,
|
|
priority: Priority.high,
|
|
),
|
|
iOS: DarwinNotificationDetails(),
|
|
),
|
|
payload: payload,
|
|
);
|
|
}
|
|
}
|