67 lines
2.3 KiB
Dart
67 lines
2.3 KiB
Dart
import 'package:flutter/material.dart';
|
|||
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||
|
|
import 'package:timezone/timezone.dart' as tz;
|
||
|
|
|
||
|
|
class NotificationService {
|
||
|
|
static final _plugin = FlutterLocalNotificationsPlugin();
|
||
|
|
static bool _initialised = false;
|
||
|
|
|
||
|
|
static const _channelId = 'daily_reminder';
|
||
|
|
static const _notificationId = 0;
|
||
|
|
|
||
|
|
static Future<void> init() async {
|
||
|
|
if (_initialised) return;
|
||
|
|
const android = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||
|
|
await _plugin.initialize(
|
||
|
|
settings: const InitializationSettings(android: android),
|
||
|
|
);
|
||
|
|
_initialised = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static Future<bool> requestPermission() async {
|
||
|
|
final android = _plugin.resolvePlatformSpecificImplementation<
|
||
|
|
AndroidFlutterLocalNotificationsPlugin>();
|
||
|
|
return (await android?.requestNotificationsPermission()) ?? false;
|
||
|
|
}
|
||
|
|
|
||
|
|
static Future<void> scheduleDaily(TimeOfDay time) async {
|
||
|
|
await _plugin.cancelAll();
|
||
|
|
const details = NotificationDetails(
|
||
|
|
android: AndroidNotificationDetails(
|
||
|
|
_channelId,
|
||
|
|
'Daily reminder',
|
||
|
|
channelDescription: 'Reminds you to log your mood',
|
||
|
|
importance: Importance.defaultImportance,
|
||
|
|
priority: Priority.defaultPriority,
|
||
|
|
),
|
||
|
|
);
|
||
|
|
await _plugin.zonedSchedule(
|
||
|
|
id: _notificationId,
|
||
|
|
title: 'DailyYou',
|
||
|
|
body: 'Time to log your mood!',
|
||
|
|
scheduledDate: _nextInstanceOfTime(time.hour, time.minute),
|
||
|
|
notificationDetails: details,
|
||
|
|
androidScheduleMode: AndroidScheduleMode.inexactAllowWhileIdle,
|
||
|
|
matchDateTimeComponents: DateTimeComponents.time,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
static Future<void> cancelAll() => _plugin.cancelAll();
|
||
|
|
|
||
|
|
static tz.TZDateTime _nextInstanceOfTime(int hour, int minute) {
|
||
|
|
final now = tz.TZDateTime.now(tz.UTC);
|
||
|
|
final offset = DateTime.now().timeZoneOffset;
|
||
|
|
var targetMinutes = hour * 60 + minute - offset.inMinutes;
|
||
|
|
// Normalise into [0, 24*60)
|
||
|
|
targetMinutes = ((targetMinutes % (24 * 60)) + 24 * 60) % (24 * 60);
|
||
|
|
final utcHour = targetMinutes ~/ 60;
|
||
|
|
final utcMin = targetMinutes % 60;
|
||
|
|
var scheduled =
|
||
|
|
tz.TZDateTime(tz.UTC, now.year, now.month, now.day, utcHour, utcMin);
|
||
|
|
if (scheduled.isBefore(now)) {
|
||
|
|
scheduled = scheduled.add(const Duration(days: 1));
|
||
|
|
}
|
||
|
|
return scheduled;
|
||
|
|
}
|
||
|
|
}
|