Flutter Package

firebase_messaging_handler

Ship reliable Flutter notifications, inboxes, badges, and click handling.

Q

Qoder

11 sectionsFlutter Package

Overview

firebase_messaging_handler turns Firebase Cloud Messaging into an application-level messaging system. It normalizes notification clicks across lifecycles, supports inbox and in-app messaging patterns, adds diagnostics, and gives teams a clearer path from raw FCM payloads to product behavior.

  • Unified notification click streams across foreground, background, and terminated app states
  • Cross-platform support for Android, iOS, Web, and desktop local-mode flows
  • Automatic token management with single backend callback
  • Smart channel fallback for Android notifications
  • Interactive notification actions with custom payloads
  • One-time and recurring notification scheduling
  • Cross-platform badge count management
  • Notification grouping (Android) and conversation threads (iOS)
  • Custom sound support per platform
  • Analytics hooks for delivery, impressions, and actions
  • Comprehensive notification diagnostics (Notification Doctor)
  • Quiet hours and frequency caps for in-app messaging
  • Data-only payload bridging to local notifications
  • Typed inbox storage with read/delete operations
  • Rich in-app templates — dialog, full-screen, bottom sheet, banner, snackbar
  • Foreground notification customization with smart fallbacks
  • Testing utilities and mock data generation

Installation

Add the package to pubspec.yaml. It wraps Firebase Messaging behavior behind a higher-level API, so teams can centralize notification setup instead of scattering lifecycle code through the app.

Dart
dependencies:
  firebase_messaging_handler: ^1.0.0

Android Setup

Add the required permissions to AndroidManifest.xml based on the features you need.

Dart
<!-- Basic notifications -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- Scheduled notifications (add these too) -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Quick Start

Initialize once at app startup, wire the backend token callback, and subscribe to a single click stream that works across app lifecycle states.

Dart
final Stream<NotificationData?>? clickStream =
    await FirebaseMessagingHandler.instance.init(
  senderId: 'your_sender_id',
  androidChannelList: [
    NotificationChannelData(
      id: 'default_channel',
      name: 'Default Notifications',
      importance: NotificationImportanceEnum.high,
      priority: NotificationPriorityEnum.high,
      playSound: true,
      enableVibration: true,
    ),
  ],
  androidNotificationIconPath: '@drawable/ic_notification',
  updateTokenCallback: (fcmToken) async {
    // Send token to your backend
    return true;
  },
);

clickStream?.listen((NotificationData? data) {
  if (data != null) {
    print('Notification clicked: ${data.title}');
  }
});

Unified Handler

Use one callback to inspect normalized payloads across foreground, background, and terminated states before deciding whether the package or your app should render the notification.

Dart
await FirebaseMessagingHandler.instance.setUnifiedMessageHandler(
  (NormalizedMessage message, NotificationLifecycle lifecycle) async {
    debugPrint('[unified] lifecycle=$lifecycle title=${message.title}');
    return false; // false = let plugin render the notification
  },
);

In-App Messaging

Deliver rich in-app experiences from silent FCM payloads using reusable templates. Register templates, then the plugin automatically triggers them when a matching payload arrives.

Dart
FirebaseMessagingHandler.instance.registerInAppNotificationTemplates({
  'builtin_generic': BuiltInInAppTemplates.generic(
    onAction: (actionId, data) {
      debugPrint('Action: $actionId');
    },
  ),
});
  • Trigger types: immediate, next_foreground, app_launch, custom
  • Built-in layouts: dialog, full_screen, bottom_sheet, banner, tooltip, carousel, snackbar
  • Quiet hours and per-template frequency caps
  • Deferred payloads are re-queued automatically

Notification Inbox

Typed, persistent inbox backed by SharedPreferences. Supports paged fetch, upsert, markRead, delete, and a built-in NotificationInboxView widget.

Dart
final inbox = InboxStorageService();
await inbox.upsert(NotificationInboxItem(
  id: 'item1',
  title: 'Message',
  timestamp: DateTime.now(),
));
final items = await inbox.fetch(page: 0, pageSize: 20);

Foreground Notification Customization

Own the fallback notification UI that appears while your app is active. Return null to use plugin defaults, or set enabled: false to suppress entirely.

Dart
FirebaseMessagingHandler.instance.setForegroundNotificationOptions(
  ForegroundNotificationOptions(
    androidBuilder: (context) => AndroidNotificationDetails(
      'promo_channel',
      'Promotions',
      styleInformation: BigPictureStyleInformation(
        DrawableResourceAndroidBitmap('large_image'),
      ),
    ),
  ),
);

Notification Diagnostics

Run the built-in notification doctor to inspect permissions, token state, badge support, web readiness, pending scheduled items, invalid payloads, and background wiring in one call.

Dart
final diagnostics =
    await FirebaseMessagingHandler.instance.runDiagnostics();
debugPrint('Diagnostics: ${diagnostics.toMap()}');
for (final rec in diagnostics.recommendations) {
  debugPrint('Recommendation: $rec');
}
  • Checks: permissionsGranted, fcmTokenAvailable, badgeSupported, backgroundHandlerRegistered
  • Web diagnostics: notification API, secure-context, service-worker
  • pendingNotificationCount for scheduled items
  • invalidPayloadCount for malformed data-only payloads
  • recommendations list with actionable fixes

Troubleshooting

Common issues and solutions.

  • No notifications: verify Firebase config files, sender ID, and AndroidManifest permissions
  • Scheduling fails: Android 13+ requires runtime permission request for SCHEDULE_EXACT_ALARM
  • iOS APNs error: configure APNs key in Apple Developer Console and upload .p8 to Firebase Console
  • Custom sounds silent: ensure files are in correct platform directories with proper formats
  • Analytics not tracking: ensure callback is set before init()
  • Debug mode: plugin logs detailed information automatically in debug builds

Testing Utilities

Mock data generation and test mode for testing without Firebase.

Dart
final mockMessage = FirebaseMessagingHandler.createMockRemoteMessage(
  title: 'Test',
  data: {'key': 'value'},
);

FirebaseMessagingHandler.setTestMode(true);
FirebaseMessagingHandler.addMockNotification(mockMessage);