Flutter Push Notifications With Firebase Cloud Messaging

Coding Liquids blog cover featuring Sagnik Bhattacharya for Flutter Push Notifications With Firebase Cloud Messaging, with notification permission and gettoken, onmessage and onmessageopenedapp, and a top-level background handler.
Coding Liquids blog cover featuring Sagnik Bhattacharya for Flutter Push Notifications With Firebase Cloud Messaging, with notification permission and gettoken, onmessage and onmessageopenedapp, and a top-level background handler.

A device recording makes the three message handlers far easier to distinguish.

Subscribe on YouTube@codingliquids

Follow the token-to-device delivery path

FCM issues a registration token, a trusted server targets that token or a topic, and the platform delivers the payload. Notification and data payloads have different background behaviour, so define which layer owns navigation.

Sending directly from a client with server credentials would expose authority to every installation.

Request notification permission at the right moment

firebase_messaging requestPermission is essential on Apple platforms and newer Android versions require runtime notification permission. Explain the benefit before the system prompt and inspect authorizationStatus after it returns.

Prompting at first launch without context increases denial and may not be repeatable.

flutter pub add firebase_messaging flutter_local_notifications

Instagram’s delivery map follows a rotating token from installation registration to backend cleanup.

Follow me on Instagram@sagnikteaches

Store tokens and observe rotation

getToken returns the current registration token and onTokenRefresh emits replacements. Send tokens to the authenticated backend with user, platform, and last-seen metadata, removing them on logout.

Treating a token as permanent produces silent delivery failures after rotation.

The Complete Flutter Guide course thumbnail

Build production-ready Push Notifications With Firebase Cloud Messaging features

The Complete Flutter Guide turns push notifications with firebase cloud messaging into maintainable app architecture, polished UI, and testable production code.

Enrol now

Messaging receives the remote payload; flutter_local_notifications supplies the foreground system alert.

dependencies:
  flutter:
    sdk: flutter
  firebase_messaging: any
  flutter_local_notifications: any

Handle foreground open and background events

onMessage covers foreground delivery, onMessageOpenedApp covers a notification tap from background, and getInitialMessage covers terminated launch. Register a top-level onBackgroundMessage handler before runApp for permitted background data work.

Navigating from every handler without deduplication can open the same route twice.

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

Show foreground alerts with local notifications

Foreground FCM messages do not automatically show the same system banner on every platform. Use flutter_local_notifications to create a channel and display title and body after checking payload data.

Displaying both an automatic notification and a local copy can produce duplicate banners.

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  print('Background message: ${message.messageId}');
}

Future<String?> configureMessaging() async {
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
  await FirebaseMessaging.instance.requestPermission(alert: true, badge: true, sound: true);
  return FirebaseMessaging.instance.getToken();
}

A LinkedIn notification essay separates permission, transport, routing, and the durable event behind each message.

Connect on LinkedInSagnik Bhattacharya
The Complete Flutter Guide course thumbnail

The Complete Flutter Guide: Build Android, iOS and Web apps

Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.

Enrol now

Subscribe devices to appropriate topics

subscribeToTopic and unsubscribeFromTopic manage broad channels such as weather_kolkata or product_updates. Topics are convenient fan-out, not private authorisation groups.

Putting confidential account data in a topic message can expose it to any subscribed installation.

FirebaseMessaging.onMessage.listen((message) {
  showForegroundNotification(message);
});
FirebaseMessaging.onMessageOpenedApp.listen(openMessageRoute);
final initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) openMessageRoute(initialMessage);

FirebaseMessaging.instance.onTokenRefresh.listen(sendTokenToServer);

Keep the background callback top-level

The background handler needs @pragma vm:entry-point, a top-level function, Firebase initialisation, and no direct UI access. Keep its work short and persist only what the foreground isolate can consume later.

A closure or instance method may be tree-shaken and cannot be invoked by the background isolate.

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  debugPrint('Background message ${message.messageId}: ${message.data}');
}

FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
await FirebaseMessaging.instance.subscribeToTopic('product_updates');

Give every navigable notification a stable message or business identifier. Before opening a route, compare that identifier with the last handled value so getInitialMessage and onMessageOpenedApp cannot process the same tap twice. Keep route arguments in the data payload because foreground local notifications and background system notifications must reconstruct the same destination.

Test delivery as a platform workflow

Notification permission is a user decision, not an installation step. Ask in context after explaining the benefit, record whether the request has already been made, and keep the app useful when permission is denied. Apple delivery also requires the correct APNs capability and Firebase association, while Android behaviour varies with notification channels and operating-system permission rules. Create channels with stable IDs because importance chosen by the user cannot be silently reset by changing a label.

The registration token can rotate after restore, reinstall, security changes, or service updates. Listen for refresh, send the current token to the authenticated backend, and associate it with a particular installation rather than assuming one token per account. Sign-out should remove or deactivate that installation mapping so the next person using the device does not receive the previous account’s messages. Server responses for invalid tokens should trigger cleanup rather than endless retries.

Background handlers have platform constraints and cannot rely on an existing widget tree. Keep the handler top-level where required, initialise only the services it needs, and finish promptly. A data message should carry a stable type and record ID rather than trusting arbitrary navigation instructions. When the user taps a notification, wait until authentication and initial routing are ready, validate access to the destination, and de-duplicate the initial-message and opened-app paths.

Exercise foreground receipt, background receipt, terminated launch, permission denial, token rotation, expired authentication, and a deleted destination record on physical devices. Emulators and consoles are useful but do not reproduce every APNs, battery, manufacturer, or release-signing condition. Log a backend message ID, installation ID, delivery path, and handling result without storing the visible private message text. Push is best treated as a hint to fetch authorised current data; delivery can be delayed, collapsed, duplicated, or absent, so it must never be the only record of an important event.

Payload design should stay small and versioned. Include a message type, schema version, and identifiers needed to fetch fresh data; reject unknown versions safely. Notification messages rendered by the platform and data messages handled by the app can behave differently by foreground state, so document which path each campaign uses. Server sends should target authorised installation records and avoid putting confidential content in lock-screen text by default. For time-sensitive actions, store the event in the backend and let the app query it after launch. That design survives delivery delay and gives a second device the same source of truth.

Localisation is more reliable when the payload carries a message type and arguments that the installed app can render, provided the required translation exists. Server-rendered text may still be appropriate for content that changes independently of releases.

Common mistakes

  • Follow the token-to-device delivery path: In Firebase Cloud Messaging, sending directly from a client with server credentials would expose authority to every installation; inspect this Firebase Cloud Messaging cause before changing another Firebase Cloud Messaging widget.
  • Request notification permission at the right moment: In Firebase Cloud Messaging, prompting at first launch without context increases denial and may not be repeatable; inspect this Firebase Cloud Messaging cause before changing another Firebase Cloud Messaging widget.
  • Store tokens and observe rotation: In Firebase Cloud Messaging, treating a token as permanent produces silent delivery failures after rotation; inspect this Firebase Cloud Messaging cause before changing another Firebase Cloud Messaging widget.
  • Handle foreground open and background events: In Firebase Cloud Messaging, navigating from every handler without deduplication can open the same route twice; inspect this Firebase Cloud Messaging cause before changing another Firebase Cloud Messaging widget.
  • Show foreground alerts with local notifications: In Firebase Cloud Messaging, displaying both an automatic notification and a local copy can produce duplicate banners; inspect this Firebase Cloud Messaging cause before changing another Firebase Cloud Messaging widget.
  • Subscribe devices to appropriate topics: In Firebase Cloud Messaging, putting confidential account data in a topic message can expose it to any subscribed installation; inspect this Firebase Cloud Messaging cause before changing another Firebase Cloud Messaging widget.

Frequently asked questions

How does follow the token-to-device delivery path work in Firebase Cloud Messaging?

For Firebase Cloud Messaging, the starting rule is that fCM issues a registration token, a trusted server targets that token or a topic, and the platform delivers the payload. Apply this Firebase Cloud Messaging rule first because follow the token-to-device delivery path determines whether the Firebase Cloud Messaging pattern fits.

Why does store tokens and observe rotation matter for Firebase Cloud Messaging?

In Firebase Cloud Messaging, send tokens to the authenticated backend with user, platform, and last-seen metadata, removing them on logout. Keeping store tokens and observe rotation at the Firebase Cloud Messaging call site exposes the Firebase Cloud Messaging return value directly.

What failure should I test first in Firebase Cloud Messaging?

First reproduce the Firebase Cloud Messaging case where displaying both an automatic notification and a local copy can produce duplicate banners. The foreground test should produce one visible alert, while background delivery remains owned by the chosen platform or application path.

How can I verify Firebase Cloud Messaging before release?

Exercise keep the background callback top-level with real Firebase Cloud Messaging inputs on every shipped platform. Inspect the final Firebase Cloud Messaging callback or output; a successful Firebase Cloud Messaging button tap alone is not proof.

Further reads

Connect Firebase Cloud Messaging to the surrounding Flutter stack through these published tutorials:

Sources: FlutterFire and Firebase documentation; Firebase Cloud Messaging examples verified against current stable Flutter.