This document dictates the reference implementation and internal execution strategies for managing local notifications in this Flutter project via flutter_local_notifications. The design follows SOLID principles utilizing a Service Locator (Singleton) pattern to enforce strict separation of concerns.
The notification operations are isolated from the UI layer to maintain immutability and testability:
- NotificationServiceImpl: A singleton implementation handling native API bridging.
- INotificationService: The abstract contract for dependency inversion and test mocking.
- Data Transfer Objects (DTOs):
NotificationModelandNotificationPayloadensure structured, type-safe execution across application boundaries.
graph TD
UI[UI Layer] -->|Calls Method| INotif[INotificationService Contract]
subgraph Service Layer [Service Layer / Singleton]
INotif -.->|Implemented By| NotifImpl[NotificationServiceImpl]
end
subgraph Data Transfer Objects [DTOs]
NotifImpl -.-> |Expected Parameter| Model[NotificationModel]
Model -.-> |Holds| Payload[NotificationPayload]
end
subgraph Native Bridge
NotifImpl -->|Method Channel| FlutterPlugin[flutter_local_notifications Plugin]
end
subgraph OS Execution
FlutterPlugin -->|Android 12/13/14 Constraints| Android[Android OS / Doze Mode]
FlutterPlugin -->|UNUserNotificationCenter| iOS[iOS OS]
end
To ensure consistent delivery across aggressive OEM restrictions (Android/iOS), the service enforces a sequential native initialization lifecycle:
- Bootstrapping: Execution occurs at the
main.dartlevel prior to mountingrunApp(). This guarantees the Flutter engine is prepared to handle background callbacks and termination-state tap vectors immediately. - Native Channel Allocation: For Android 8.0+, dedicated native channels (
instant_channel,scheduled_channel) are established withImportance.max. This overrides native OS suppression, ensuring Heads-Up displays and audible alerts. - Permission Handshake: Runtime verification is strategically enforced. Missing permissions will execute Dart code without throwing exceptions but will result in silent drops by the native OS if not explicitly handled here.
- Dispatch: Timezone-aware dispatch mechanisms prevent drift due to geographical timezone alterations by strictly anchoring schedules to
tz.local.
The scheduling engine explicitly defaults to AndroidScheduleMode.inexactAllowWhileIdle.
- Justification: Android 12 (API 31) and Android 14 proactively restrict
SCHEDULE_EXACT_ALARM. Deploying exact alarms without elevated OS-level permissions throws a fatalSecurityException. - System Behavior: Utilizing
inexactAllowWhileIdleensures the notification safely wakes the device from Doze mode, bypassing extreme fallback complexities while maintaining a dependable delivery window for non-calendar applications.
- Android 13+ (API 33):
POST_NOTIFICATIONSis strictly enforced. Native execution proceeds without exception if this is omitted in Dart, but the Android OS drops the payload silently. Requesting this explicit permission prevents untraceable failures. - iOS Restrictions: Apple's
UNUserNotificationCenterstrictly halts any alert, badge, or sound execution without prior explicit authorization from the user.
Instant Dispatch:
await NotificationServiceImpl().showInstantNotification(
NotificationModel(
id: 1,
title: 'System Alert',
body: 'Execution completed successfully.',
type: NotificationType.instant,
),
);Scheduled Dispatch:
await NotificationServiceImpl().scheduleNotification(
NotificationModel(
id: 2,
title: 'System Reminder',
body: 'Automated task scheduled.',
type: NotificationType.scheduled,
scheduledDateTime: DateTime.now().add(const Duration(hours: 1)),
),
);