Why Push Notifications Are Essential for Mobile Engagement
Push notifications are the primary channel for re-engaging mobile users after they leave your app. Studies show that apps with push notifications achieve 3–7x higher retention rates than those without. For e-commerce, notifications drive abandoned cart recovery. For social apps, they deliver real-time conversation updates. For news apps, they break stories as they happen. In Flutter, Firebase Cloud Messaging (FCM) provides the cross-platform infrastructure for delivering push notifications to both Android and iOS devices from a single backend, with support for both notification messages (displayed by the OS) and data messages (handled by your app code).
Firebase Project Setup and Platform Configuration
Create a Firebase project in the Firebase Console. Add both Android and iOS apps. For Android: download `google-services.json`, place it in `android/app/`, and add the Google Services plugin to `android/build.gradle`. For iOS: download `GoogleService-Info.plist`, add it to the Xcode project via Xcode (not file copy), enable Push Notifications capability in Signing & Capabilities, and enable Background Modes with Remote notifications checked. Add dependencies to `pubspec.yaml`: `firebase_core`, `firebase_messaging`, and `flutter_local_notifications`. Initialize Firebase before `runApp()`: `await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform)`.
Requesting Notification Permissions on iOS and Android 13+
iOS requires explicit user permission for notifications. Call `await FirebaseMessaging.instance.requestPermission(alert: true, badge: true, sound: true)` early in the app lifecycle—ideally after explaining the value of notifications to the user (a pre-permission screen). Check the returned `AuthorizationStatus`: `.authorized`, `.denied`, `.provisional`, or `.notDetermined`. Android 13+ (API level 33) also requires runtime permission: `POST_NOTIFICATIONS`. Add `
FCM Device Tokens: Registration and Management
Every device receives a unique FCM registration token. Retrieve it: `String? token = await FirebaseMessaging.instance.getToken()`. Send this token to your backend server for storage. The backend uses this token to target specific devices when sending notifications. Tokens can change—handle token refresh: `FirebaseMessaging.instance.onTokenRefresh.listen((newToken) { sendTokenToServer(newToken); })`. For topic-based messaging, subscribe devices to topics: `await FirebaseMessaging.instance.subscribeToTopic('breaking_news')`. Your backend sends a single API call to the topic, and FCM delivers to all subscribed devices—eliminating the need to manage individual tokens for broadcast messages.
Handling Foreground Notifications with Local Notifications
On Android, FCM notification messages are automatically displayed by the OS when the app is in the background. But in the foreground, notification messages are delivered to your app code silently—no system notification appears. Use `flutter_local_notifications` to display them manually: create a `FlutterLocalNotificationsPlugin`, initialize with Android and iOS settings, create a notification channel, and call `show()` with the notification title and body. Listen for foreground messages: `FirebaseMessaging.onMessage.listen((RemoteMessage message) { showLocalNotification(message); })`. On iOS, call `await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(alert: true, badge: true, sound: true)` to display notifications even in foreground.
Transform Your Publishing Workflow
Our experts can help you build scalable, API-driven publishing systems tailored to your business.
Background and Terminated State Message Handling
FCM handles three app states differently. Foreground: messages arrive via `onMessage` stream. Background: the OS displays notification messages automatically; data messages are handled by the background handler. Terminated: when the user taps a notification that launched the app, retrieve it via `FirebaseMessaging.instance.getInitialMessage()`. Register the background handler as a top-level function (not a class method): `@pragma('vm:entry-point') Future
Deep Linking: Navigating to Specific Screens on Tap
Notifications should navigate users to relevant content when tapped. Include a data payload with routing information: `{ "route": "/order/12345" }`. Handle notification taps in three scenarios: `getInitialMessage()` for terminated state, `onMessageOpenedApp` for background state, and tap callbacks from `flutter_local_notifications` for foreground. Create a centralized handler: `void handleNotificationNavigation(RemoteMessage message) { final route = message.data['route']; if (route != null) navigatorKey.currentState?.pushNamed(route); }`. Use a `GlobalKey
Server-Side: Sending Notifications via FCM REST API
Send notifications from your backend using the FCM HTTP v1 API. Authenticate with a Google service account. Send to a specific device: `POST https://fcm.googleapis.com/v1/projects/{project_id}/messages:send` with body `{ "message": { "token": "device_token", "notification": { "title": "Order Shipped", "body": "Your order #12345 is on its way" }, "data": { "route": "/order/12345" } } }`. Send to a topic: replace `token` with `topic: "breaking_news"`. Use conditional messaging to target device groups: `condition: "'topic_a' in topics && 'topic_b' in topics"`. Schedule notifications using Cloud Functions with Cloud Scheduler for time-based delivery.




