Skip to content

Instantly share code, notes, and snippets.

@lukepighetti
Last active February 7, 2025 13:53
Show Gist options
  • Save lukepighetti/a7c44cb921b62876d4be18394246695e to your computer and use it in GitHub Desktop.
Save lukepighetti/a7c44cb921b62876d4be18394246695e to your computer and use it in GitHub Desktop.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
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;
extension EasyFlutterLocalNotifications on FlutterLocalNotificationsPlugin {
Future<void> easyInitialize() async {
tz.initializeTimeZones();
await initialize(
const InitializationSettings(
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
iOS: DarwinInitializationSettings(),
),
);
}
Future<void> easySchedule({
bool enabled = true,
required int id,
required DateTime date,
required String title,
required String body,
String androidChannelId = 'notifications',
String androidChannelName = 'Notifications',
NotificationLevel level = NotificationLevel.normal,
}) async {
if (enabled) {
await zonedSchedule(
id,
title,
body,
tz.TZDateTime.from(date, tz.local),
level.buildDetails(
androidChannelId: androidChannelId,
androidChannelName: androidChannelName,
),
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
androidScheduleMode: AndroidScheduleMode.exact,
);
} else {
easyRetainOrClear(retain: false, targetIds: [id]);
}
if (kDebugMode) {
final pending = await pendingNotificationRequests();
debugPrint(
'[EasyFlutterNotifications]: easySchedule(id: $id, date: $date, title: $title, body: $body), pending: ${pending.length}',
);
}
}
Future<void> easySchedulePeriodic({
int n = 7,
bool enabled = true,
required int idPrefix,
required DateTime start,
required Duration interval,
required String Function(int i) title,
required String Function(int i) body,
String androidChannelId = 'notifications',
String androidChannelName = 'Notifications',
NotificationLevel level = NotificationLevel.normal,
}) async {
assert(n <= 10);
if (enabled) {
for (var i = 0; i < n; i++) {
await easySchedule(
id: idPrefix * 10 + i,
date: start.add(interval * i),
title: title(i),
body: body(i),
);
}
} else {
easyRetainOrClear(retain: false, targetIdPrefixes: [idPrefix]);
}
}
Future<void> easyScheduleDaily({
int n = 7,
bool enabled = true,
required int idPrefix,
required TimeOfDay time,
required String Function(int i) title,
required String Function(int i) body,
String androidChannelId = 'notifications',
String androidChannelName = 'Notifications',
NotificationLevel level = NotificationLevel.normal,
}) async {
await easySchedulePeriodic(
enabled: enabled,
idPrefix: idPrefix,
start: time.nextDateFromNow,
interval: const Duration(days: 1),
title: title,
body: body,
androidChannelId: androidChannelId,
androidChannelName: androidChannelName,
level: level,
);
}
Future<void> easyRetainOrClear({
required bool retain,
List<int> targetIds = const [],
List<int> targetIdPrefixes = const [],
}) async {
for (final x in await pendingNotificationRequests()) {
final matchId = targetIds.contains(x.id);
final matchIdPrefix = targetIdPrefixes
.map((e) => e * 10)
.any((e) => e <= x.id && x.id <= e + 9);
if ((matchId || matchIdPrefix) && !retain) {
await cancel(x.id);
} else {
continue;
}
}
if (kDebugMode) {
final pending = await pendingNotificationRequests();
debugPrint(
"[EasyFlutterLocalNotifications]: easyRetainOrClear(retain: $retain, targetIds: $targetIds, targetIdPrefixes: $targetIdPrefixes), pending: ${pending.length}",
);
}
}
}
enum NotificationLevel {
normal(
importance: Importance.defaultImportance,
interruptionLevel: InterruptionLevel.active,
lights: true,
playSound: true,
priority: Priority.defaultPriority,
vibrate: true,
visibility: NotificationVisibility.public,
),
low(
importance: Importance.min,
interruptionLevel: InterruptionLevel.active,
lights: false,
playSound: false,
priority: Priority.low,
vibrate: false,
visibility: NotificationVisibility.private,
),
stealth(
importance: Importance.min,
interruptionLevel: InterruptionLevel.passive,
lights: false,
playSound: false,
priority: Priority.min,
vibrate: false,
visibility: NotificationVisibility.secret,
),
;
final Importance importance;
final InterruptionLevel interruptionLevel;
final bool lights;
final bool playSound;
final Priority priority;
final bool vibrate;
final NotificationVisibility visibility;
const NotificationLevel({
required this.importance,
required this.interruptionLevel,
required this.lights,
required this.playSound,
required this.priority,
required this.vibrate,
required this.visibility,
});
NotificationDetails buildDetails({
required String androidChannelId,
required String androidChannelName,
}) {
final androidNotificationDetails = AndroidNotificationDetails(
androidChannelId,
androidChannelName,
importance: importance,
priority: priority,
playSound: playSound,
enableLights: lights,
enableVibration: vibrate,
visibility: visibility,
);
final darwinNotificationDetails = DarwinNotificationDetails(
presentSound: playSound,
interruptionLevel: interruptionLevel,
);
return NotificationDetails(
android: androidNotificationDetails,
iOS: darwinNotificationDetails,
);
}
}
extension on DateTime {
TimeOfDay get time => TimeOfDay(hour: hour, minute: minute);
DateTime atTime(TimeOfDay x) => copyWith(
hour: x.hour,
minute: x.minute,
second: 0,
millisecond: 0,
microsecond: 0,
);
}
extension on TimeOfDay {
DateTime get nextDateFromNow {
final now = DateTime.now();
if (isAfter(now.time)) {
return now.atTime(this);
} else {
return now.add(const Duration(days: 1)).atTime(this);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment