arch-atlas

NotificationService (FCM + Local)

core/services/notification_service.dart

data

Overview

Singleton wrapping FirebaseMessaging + flutter_local_notifications. Handles: permission request, foreground listener (shows a local notification), background handler, token retrieval/refresh callback, terminated-state initial-message check, topic sub/unsub.

User Flow

  1. initialize() requests permission, registers background handler, subscribes to onMessage, sets up local notifications channel.
  2. Foreground messages with a notification payload are mirrored into a local notification via _showNotification.
  3. FirebaseMessaging.onTokenRefresh forwards to _onTokenRefresh callback registered by DioHelper.syncFcmTokenForAllRoles.
  4. Background handler is a top-level @pragma('vm:entry-point') function — must stay at file scope.
  5. deleteToken is invoked on logout (AuthStateNotifier).

Cases & Edge Cases

  • Android creates a high-importance channel ('high_importance_channel') — required for heads-up display.
  • iOS requires presentAlert/presentBadge/presentSound to be true at show time, otherwise the local notification is silent.
  • Permission denial silently fails — initialize still completes; getToken may return null. UI shows no notifications without surfacing this to the user yet.
  • Topic subscribe/unsubscribe is exposed but not used today.

Code References

  • lib/src/core/services/notification_service.dart:8 // _firebaseMessagingBackgroundHandler
  • lib/src/core/services/notification_service.dart:15 // NotificationService
  • lib/src/core/services/notification_service.dart:33 // initialize
  • lib/src/core/services/notification_service.dart:121 // _initLocalNotifications
  • lib/src/core/services/notification_service.dart:180 // deleteToken

API Calls

Empty — fill this in later. Open the manifest and add content here.

Notes

Initialize is invoked from main.dart before runApp; flutter_local_notifications must initialize after Firebase to share channel IDs cleanly. Native SDK contracts (no HTTP — Firebase + local notifications plugin): • FirebaseMessaging.requestPermission(alert,badge,sound,…) → NotificationSettings { authorizationStatus } — screen: app boot (main.dart) • FirebaseMessaging.getToken() → String? (device FCM token) — screen: app boot + DioHelper._checkAndSendFcmToken • FirebaseMessaging.onTokenRefresh → Stream<String> — screen: app boot (wires into DioHelper.syncFcmTokenForAllRoles) • FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler) — top-level entry-point — screen: app boot (debug log only) • FirebaseMessaging.onMessage.listen → RemoteMessage { notification: { title, body }, data: {…} } — screen: app boot → _showNotification (mirrors to local notif) • FirebaseMessaging.onMessageOpenedApp / getInitialMessage() → RemoteMessage — screen: app boot (deep-link to /orders/:id, etc.) • flutter_local_notifications.show(id, title, body, NotificationDetails(...)) → void — channel 'high_importance_channel'.