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 queued again 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);