Skip to content

vipulbansal/firebase_messaging_manager

Repository files navigation

firebase_messaging_manager

A production-safe Firebase Messaging lifecycle manager for Flutter that helps you:

  • handle foreground, background, and terminated notification flows
  • orchestrate firebase_messaging with flutter_local_notifications
  • prevent duplicate notifications using configurable dedupe strategies
  • request and check notification permissions on Android and iOS
  • receive unified typed notification events through clean callbacks

Features

  • Notification lifecycle handling (foreground, background, terminated)
  • Notification tap handling from background and terminated launch
  • Duplicate notification prevention (none, messageId, customKey)
  • Configurable dedupe duration
  • Local notification orchestration with Android notification channels
  • Unified NotificationEvent model for all callbacks
  • Optional debug logs

Installation

Add the package:

dependencies:
  firebase_messaging_manager: ^0.0.1

Then run:

flutter pub get

Firebase Setup

1) Add Firebase to your app

Set up Firebase for Android and iOS in your app using the official Firebase docs:

Recommended CLI flow:

dart pub global activate flutterfire_cli
flutterfire configure

2) Initialize Firebase before using messaging

await Firebase.initializeApp();

Android Setup

  1. Add notification permission for Android 13+ in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
  1. Ensure internet permission exists:
<uses-permission android:name="android.permission.INTERNET" />
  1. Keep a valid launcher icon (@mipmap/ic_launcher) or pass a custom icon via androidNotificationIcon.

  2. Keep notification channel IDs stable in production (androidChannelId, androidChannelName).

iOS Setup

  1. In Xcode (ios/Runner.xcworkspace) enable capabilities:
  • Push Notifications
  • Background Modes -> Remote notifications
  1. Ensure APNs is configured in Firebase Console (key or certificate).

  2. In ios/Runner/Info.plist, ensure notification usage descriptions are present for your app if needed by your compliance/review flow.

  3. For iOS foreground delivery behavior, this package sets:

  • alert: true
  • badge: true
  • sound: true

Quick Start

import 'package:firebase_messaging_manager/firebase_messaging_manager.dart';

Future<void> initializePushNotifications() async {
  await FirebaseMessagingManager.instance.initialize(
    config: const FirebaseMessagingManagerConfig(
      requestPermission: true,
      showForegroundNotifications: true,
      showBackgroundNotifications: false,
      duplicateStrategy: DuplicateStrategy.messageId,
      dedupeDuration: Duration(seconds: 10),
      androidChannelId: 'high_importance_notifications',
      androidChannelName: 'High Importance Notifications',
      additionalAndroidChannels: <AndroidNotificationChannelConfig>[
        AndroidNotificationChannelConfig(
          id: 'chat_messages',
          name: 'Chat Messages',
          description: 'Conversation and message alerts',
        ),
        AndroidNotificationChannelConfig(
          id: 'marketing_updates',
          name: 'Marketing Updates',
          description: 'Promotions and offers',
        ),
      ],
      enableLogs: true,
    ),
    onForegroundMessage: (event) async {
      // Update UI, badges, in-app state, etc.
    },
    onNotificationTap: (event) async {
      // Route user to a screen using event.data
    },
    onInitialNotification: (event) async {
      // Handle terminated -> app launched from notification tap
    },
    onBackgroundMessage: (event) async {
      // Handle data-only background processing if needed
    },
  );
}

Multiple Android Channels (Categories)

For apps with categories (chat, marketing, orders, alerts), you can register multiple channels and route events to a specific channel:

await FirebaseMessagingManager.instance.initialize(
  config: FirebaseMessagingManagerConfig(
    androidChannelId: 'general',
    androidChannelName: 'General',
    additionalAndroidChannels: const [
      AndroidNotificationChannelConfig(id: 'chat', name: 'Chat'),
      AndroidNotificationChannelConfig(id: 'orders', name: 'Orders'),
      AndroidNotificationChannelConfig(id: 'marketing', name: 'Marketing'),
    ],
    resolveAndroidChannelId: (event) {
      final type = event.data['type']?.toString();
      if (type == 'chat') return 'chat';
      if (type == 'order') return 'orders';
      if (type == 'marketing') return 'marketing';
      return null; // falls back to default channel
    },
  ),
);

What initialize(...) Actually Does

When you call FirebaseMessagingManager.instance.initialize(...), the package:

  • optionally requests notification permission (requestPermission: true)
  • configures iOS foreground presentation options
  • initializes local notification support (flutter_local_notifications)
  • creates the configured Android notification channel
  • registers FCM listeners for foreground, tap-from-background, and terminated-launch flows
  • registers background message handling
  • normalizes all incoming events to NotificationEvent
  • applies duplicate-prevention checks before invoking callbacks

Local Notification Behavior (Important)

This package is not only callback wiring. It also orchestrates local notifications.

  • If showForegroundNotifications is true, foreground messages can trigger a local notification.
  • If showBackgroundNotifications is true, background data-only messages can trigger a local notification.
  • Android local notifications use androidChannelId and androidChannelName.
  • Duplicate checks are applied before callbacks and local-notification display.

In short: callbacks are hooks for your app logic, while display/orchestration can be handled by package config.

Callback Behavior (Who Does What)

onForegroundMessage

  • Trigger: FCM message while app is in foreground.
  • Package: normalize -> dedupe -> optionally show local notification.
  • Your callback: app logic (update UI, refresh data, analytics, etc.).

onNotificationTap

  • Trigger: user taps notification and app opens from background.
  • Package: normalize -> dedupe.
  • Your callback: navigation/routing/business logic.

onInitialNotification

  • Trigger: app launched from terminated state via notification tap.
  • Package: normalize -> dedupe.
  • Your callback: startup routing/deeplink handling.
  • Fallback: if not provided, package falls back to onNotificationTap.

onBackgroundMessage

  • Trigger: background message handler path.
  • Package: normalize -> dedupe -> optional local notification for data-only payloads (when enabled).
  • Your callback: background-safe app work (sync/cache/light processing).

Integration Checklist

  • Call WidgetsFlutterBinding.ensureInitialized() before Firebase init.
  • Call Firebase.initializeApp() before lifecycle manager init.
  • Initialize FirebaseMessagingManager once during app startup.
  • Register callbacks for foreground/tap/initial/background states.
  • Verify dedupe strategy against your payload shape (messageId or custom key).
  • Test on real devices for both Android and iOS.

Notification Event Model

All callbacks use NotificationEvent:

class NotificationEvent {
  final String? id;
  final String? title;
  final String? body;
  final Map<String, dynamic> data;
  final NotificationAppState appState;
  final bool wasTapped;
  final RemoteMessage rawMessage;
}

Duplicate Prevention

Choose strategy from FirebaseMessagingManagerConfig:

  • DuplicateStrategy.none: no dedupe
  • DuplicateStrategy.messageId: dedupe by FCM message ID (fallback fingerprint if ID missing)
  • DuplicateStrategy.customKey: dedupe by a payload key (customDuplicateKey)

Example:

const FirebaseMessagingManagerConfig(
  duplicateStrategy: DuplicateStrategy.customKey,
  customDuplicateKey: 'notification_id',
  dedupeDuration: Duration(seconds: 15),
);

Foreground, Background, Terminated Behavior

  • Foreground: onMessage is normalized to NotificationEvent; local notification can be shown.
  • Background: tap events are delivered through onNotificationTap.
  • Terminated: launch payload is delivered through onInitialNotification.

Permissions

You can let the package request permission during initialization or call helpers manually:

final status = await FirebaseMessagingManager.instance.getPermissionStatus();
final settings = await FirebaseMessagingManager.instance.requestPermission();

Common Issues

  • No notifications on iOS: verify APNs key/cert and iOS capabilities.
  • No foreground banners: ensure showForegroundNotifications is true.
  • Duplicate messages: choose a stricter dedupe strategy and verify payload identifiers.
  • Tap callbacks not firing: ensure initialize() is called once early in app startup.
  • Data-only background flow not observed: verify payload is truly data-only and app/background constraints are satisfied.

FAQ

Does this replace Firebase Messaging?

No. It wraps firebase_messaging and flutter_local_notifications into a cleaner lifecycle API.

Can I still access raw RemoteMessage?

Yes, each NotificationEvent includes rawMessage.

Is this package opinionated about routing?

No, routing decisions stay in your callback handlers.

Roadmap

  • Additional sample scenarios in example/ (routing and state restoration)
  • More advanced Android/iOS platform customization
  • Expanded testing for lifecycle edge cases

About

firebase_messaging_manager is a Flutter package that streamlines Firebase push notifications on Android and iOS. It unifies foreground, background, tap, and terminated lifecycle handling, prevents duplicates, supports local notifications and Android channels, handles permissions, and provides clean typed callbacks for easy app routing.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages