Software Engineering & Digital Products for Global Enterprises since 2006
CMMi Level 3SOC 2ISO 27001
Menu
View all services
Staff Augmentation
Embed senior engineers in your team within weeks.
Dedicated Teams
A ring-fenced squad with PM, leads, and engineers.
Build-Operate-Transfer
We hire, run, and transfer the team to you.
Contract-to-Hire
Try the talent. Convert when you're ready.
ForceHQ
Skill testing, interviews and ranking — powered by AI.
RoboRingo
Build, deploy and monitor voice agents without code.
MailGovern
Policy, retention and compliance for enterprise email.
Vishing
Test and train staff against AI-driven voice attacks.
CyberForceHQ
Continuous, adaptive security training for every team.
IDS Load Balancer
Built for Multi Instance InDesign Server, to distribute jobs.
AutoVAPT.ai
AI agent for continuous, automated vulnerability and penetration testing.
Salesforce + InDesign Connector
Bridge Salesforce data into InDesign to design print catalogues at scale.
View all solutions
Banking, Financial Services & Insurance
Cloud, digital and legacy modernisation across financial entities.
Healthcare
Clinical platforms, patient engagement, and connected medical devices.
Pharma & Life Sciences
Trial systems, regulatory data, and field-force enablement.
Professional Services & Education
Workflow automation, learning platforms, and consulting tooling.
Media & Entertainment
AI video processing, OTT platforms, and content workflows.
Technology & SaaS
Product engineering, integrations, and scale for tech companies.
Retail & eCommerce
Shopify, print catalogues, web-to-print, and order automation.
View all industries
Blog
Engineering notes, opinions, and field reports.
Case Studies
How clients shipped — outcomes, stack, lessons.
White Papers
Deep-dives on AI, talent models, and platforms.
Portfolio
Selected work across industries.
View all resources
About Us
Who we are, our story, and what drives us.
Co-Innovation
How we partner to build new products together.
Careers
Open roles and what it's like to work here.
News
Press, announcements, and industry updates.
Leadership
The people steering MetaDesign.
Locations
Gurugram, Brisbane, Detroit and beyond.
Contact Us
Talk to sales, hiring, or partnerships.
Request TalentStart a Project
Mobile Development

Integrating Push Notifications in Flutter Apps with Code Samples

AG
Amit Gupta
Founder & CEO
April 18, 2023
10 min read
Integrating Push Notifications in Flutter Apps with Code Samples — Mobile Development | MetaDesign Solutions

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 `` to AndroidManifest.xml and request it at runtime. For Android 12 and below, permissions are granted by default at install time.

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.

Book a free consultation

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 _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); // process message }`. Register it: `FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler)`. This runs in an isolate with limited access to app state.

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` to access the navigator from outside the widget tree.

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.

FAQ

Frequently Asked Questions

Common questions about this topic, answered by our engineering team.

Create a Firebase project, add platform-specific config files (google-services.json for Android, GoogleService-Info.plist for iOS), add firebase_core and firebase_messaging dependencies, initialize Firebase before runApp(), request permissions, and retrieve the FCM device token.

FCM notification messages are silently delivered to your app code in the foreground—no system notification appears. Use flutter_local_notifications to manually display them by listening to FirebaseMessaging.onMessage and calling show() with the notification content.

Include routing data in the notification payload. Handle taps via getInitialMessage() (terminated state), onMessageOpenedApp (background state), and flutter_local_notifications tap callbacks (foreground). Use a centralized handler with a GlobalKey navigator.

Notification messages are displayed by the OS automatically (background/terminated) or delivered silently (foreground). Data messages are always delivered to your app code for custom handling. You can send both types together for maximum flexibility.

Use the FCM HTTP v1 API with Google service account authentication. Send to specific devices using tokens, broadcast to topics, or use conditional targeting. Schedule notifications with Cloud Functions and Cloud Scheduler for time-based delivery.

Discussion

Join the Conversation

Ready when you are

Let's build something great together.

A 30-minute call with a principal engineer. We'll listen, sketch, and tell you whether we're the right partner — even if the answer is no.

Talk to a strategist
Need help with your project? Let's talk.
Book a call