NotificationService class

Central service for managing notifications in Dreamic-based apps.

This service handles:

  • FCM (Firebase Cloud Messaging) message receiving and parsing
  • Local notification display
  • Notification permission management
  • Notification routing and deep linking
  • Badge count management
  • Rich notifications (images, actions)

Separation of Concerns (DeviceService Integration)

NotificationService owns the FCM token lifecycle:

  • Token acquisition (initial fetch from Firebase Messaging)
  • Token refresh (listening to Firebase token refresh events)
  • Token caching (in-memory and SharedPreferences)
  • Local notification state and permission prompting

NotificationService does NOT own backend persistence. When DeviceServiceInt is registered in GetIt, token changes are delegated to DeviceServiceInt.persistFcmToken for Firestore writes.

Critical: Logout Path

On logout, NotificationService performs local cleanup only (clear cached token, stop listeners, delete Firebase token). Backend cleanup is owned by DeviceService, which deletes the device doc via its own onAboutToLogOut callback. NotificationService must NOT call DeviceServiceInt.persistFcmToken during logout to avoid racing with the device doc deletion.

Usage

Initialize the service early in your app startup:

await NotificationService().initialize(
  onNotificationTapped: (route, data) {
    if (route != null) {
      appRouter.navigateNamed(route, arguments: data);
    }
  },
);

Request permissions when appropriate:

final status = await NotificationService().requestPermissions();

Optional Feature

This service is completely optional. Apps that don't use notifications:

  • Don't need to import or initialize this service
  • Don't need notification entitlements
  • Won't have notification code in their build (tree-shaking)

Constructors

NotificationService()
Gets the singleton instance of NotificationService.
factory

Properties

badgeCountStream Stream<int>
Stream of badge count changes.
no setter
cachedFcmToken String?
Gets the current cached FCM token.
no setter
channelManager NotificationChannelManager?
Gets the channel manager for advanced channel operations.
no setter
handlingDeepLinkForTesting bool
getter/setter pair
hasFcmTokenInitializedForTesting bool
getter/setter pair
hashCode int
The hash code for this object.
no setterinherited
isConnectedToAuth bool
Whether this service is connected to auth.
no setter
isFcmTokenInitialized bool
Whether FCM token management has been initialized.
no setter
isInitialized bool
Whether the service has been initialized.
no setter
lifecycleResumeHandlerActiveForTesting bool
getter/setter pair
onSystemNotificationSettingsOpenedForTesting NotificationSettingsDeepLinkCallback?
no getter
onSystemNotificationSettingsOpenedGetterForTesting NotificationSettingsDeepLinkCallback?
Gets _onSystemNotificationSettingsOpened for test assertions (e.g., verifying cleanup nulls it out).
no setter
onTokenChangedForTesting Future<void> Function(String?, String?)?
no getter
permissionHelper NotificationPermissionHelper
Access the permission helper for advanced permission flow control.
no setter
providesAppNotificationSettingsForTesting bool
Exposes the computed value of providesAppNotificationSettings that is passed to FirebaseMessaging.instance.requestPermission in requestPermissions. Tests can verify this value without requiring Firebase initialization.
no setter
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
settingsChannelForTesting MethodChannel?
Gets _settingsChannel for test assertions.
getter/setter pair
showNotificationsInForeground bool
Whether to show notifications when app is in foreground.
no setter
testGetGoToSettingsPromptInfoOverride Future<GoToSettingsPromptInfo?> Function()?
no getter
testGetNotificationDenialInfoOverride Future<NotificationDenialInfo?> Function()?
no getter
testGetPermissionStatusOverride Future<NotificationPermissionStatus> Function()?
no getter
testGetValuePropDeclineInfoOverride Future<ValuePropDeclineInfo?> Function()?
no getter
testInitializeFcmTokenOverride Future<void> Function()?
no getter
waitingForSettingsReturnForTesting bool
getter/setter pair

Methods

cancelAllNotifications() Future<void>
Cancels all notifications.
cancelNotification(int id) Future<void>
Cancels a specific notification by ID.
checkPendingSettingsIntentForTesting() Future<void>
Exposes the pending intent check logic from initialize for direct testing. Unlike the production path in initialize, this does NOT use addPostFrameCallback — the deep link handler is invoked directly. This is appropriate for unit tests where the widget tree is not relevant.
clearBadge() Future<void>
Clears the app icon badge.
clearFcmToken() Future<void>
Clears the stored FCM token. Call this on sign out.
clearGoToSettingsPromptInfo() Future<void>
Clears stored "go to settings" prompt info.
clearNotificationDenialInfo() Future<void>
Clears stored denial info (e.g., after user grants permission via settings).
clearValuePropDeclineInfo() Future<void>
Clears stored value-proposition decline info.
connectToAuthService({AuthServiceInt? authService, Future<void> onTokenChanged(String? newToken, String? oldToken)?}) Future<void>
Connects to an auth service to automatically manage FCM tokens on login/logout.
disableNotifications() Future<void>
Disables notifications at the app level.
dispose() Future<void>
Disposes of the service and cleans up resources.
enableNotifications() Future<NotificationInitResult>
Enables notifications at the app level.
getActiveNotifications() Future<List<ActiveNotification>>
Gets a list of currently active (displayed) notifications.
getBadgeCount() int
Gets the current badge count synchronously.
getGoToSettingsPromptInfo() Future<GoToSettingsPromptInfo?>
Gets metadata about previous "go to settings" prompts.
getNotificationDenialInfo() Future<NotificationDenialInfo?>
Gets metadata about previous notification permission denials.
getPermissionDenialCount() Future<int>
Gets the number of times permissions have been denied.
getPermissionRequestCount() Future<int>
Gets the number of times permissions have been requested.
getPermissionStatus() Future<NotificationPermissionStatus>
Gets the current notification permission status.
getValuePropDeclineInfo() Future<ValuePropDeclineInfo?>
Gets metadata about previous value-proposition declines.
handleAboutToLogOut() Future<void>
Public handler for pre-logout cleanup.
handleAuthenticated(String? uid) Future<void>
Public handler for auth state changes. Called when user authenticates.
handleSettingsDeepLinkForTesting(String? channelId) Future<void>
Exposes _handleSettingsDeepLink for direct testing.
handleSettingsMethodCallForTesting(MethodCall call) Future
Exposes _handleSettingsMethodCall for direct testing.
initialize({NotificationActionCallback? onNotificationTapped, NotificationButtonActionCallback? onNotificationAction, ForegroundMessageCallback? onForegroundMessage, NotificationErrorCallback? onError, bool showNotificationsInForeground = true, int reminderIntervalDays = 30, Future<void> onTokenChanged(String? newToken, String? oldToken)?, NotificationSettingsDeepLinkCallback? onSystemNotificationSettingsOpened, AuthServiceInt? authService, bool autoConnectAuth = true}) Future<void>
Initializes the notification service.
initializeFcmToken({required Future<void> onTokenChanged(String? newToken, String? oldToken)}) Future<void>
Initializes FCM token management and syncs with server.
initializeNotifications() Future<NotificationInitResult>
Initialize notifications manually.
isNotificationsEnabled() Future<bool>
Returns whether notifications are enabled at the app level.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
openNotificationSettings() Future<bool>
Opens the system notification settings page for this app.
openSystemSettings() Future<void>
Opens the system settings page for this app.
preLogoutCleanup({Duration timeout = const Duration(seconds: 5)}) Future<void>
Performs pre-logout cleanup for notifications.
recordValuePropDecline() Future<void>
Records a value-proposition decline.
requestPermissions({bool provisional = false}) Future<NotificationPermissionStatus>
Requests notification permissions from the user.
runNotificationPermissionFlow(BuildContext context, {NotificationFlowConfig? config}) Future<NotificationFlowResult>
Run the complete notification permission flow with built-in dialogs.
setupLifecycleListenerForTesting() → void
Exposes _setupLifecycleListener for direct testing of lifecycle polling behavior (race prevention, duplicate entry guard, etc.).
shouldShowPeriodicReminder() Future<bool>
Checks if enough time has passed to show a permission reminder.
shouldShowValuePropReminder({Duration? cooldown, int? maxAskCount}) Future<bool>
Returns true if a value-proposition reminder should be shown.
showNotification(NotificationPayload payload) Future<int>
Displays a local notification.
toString() String
A string representation of this object.
inherited
updateBadgeCount(int count) Future<void>
Updates the app icon badge count.
updateLastReminderDate() Future<void>
Updates the last reminder date to now.

Operators

operator ==(Object other) bool
The equality operator.
inherited

Static Methods

resetForTesting() → void
Resets the singleton instance for testing. Call in tearDown().

Constants

notificationSettingsChannelName → const String
Method channel for receiving notification settings deep links from native code.