+91 7976 955 311
hello@fbipool.com
+91 7976 955 311
hello@fbipool.com
Ever opened an app and wondered how it magically knows to buzz your phone with the perfect message at just the right moment? That’s the power of Flutter Firebase push notifications working behind the scenes.
Picture this: your user just abandoned their shopping cart, and 30 minutes later, they get a gentle nudge about those items waiting for them. Or imagine sending breaking news alerts that actually get opened because they’re timed perfectly.
Push notifications aren’t just alerts – they’re your direct line to user engagement, retention, and ultimately, business success.
In this tutorial, we’ll walk through implementing Firebase Cloud Messaging (FCM) in Flutter from scratch, covering everything from basic setup to advanced features like background notifications and deep linking.
Firebase Cloud Messaging Flutter integration allows you to send targeted messages to users across Android and iOS devices, whether your app is running in the foreground, background, or completely closed.
Think of FCM as your app’s personal messenger service. It handles the heavy lifting of message delivery while you focus on crafting the perfect user experience.
Here’s what makes FCM special:
Before diving into FCM integration in Flutter, let’s make sure you have everything ready.
Pro tip: Create a test project first. This way, you can experiment freely without affecting any production apps.
Let’s get the Firebase messaging package setup sorted. This is where the magic begins.
Open your pubspec.yaml file and add these packages:
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.24.2
firebase_messaging: ^14.7.10
flutter_local_notifications: ^16.3.0
Run flutter pub get to install everything.
In your main.dart file, initialize Firebase before running your app:
import ‘package:firebase_core/firebase_core.dart’;
import ‘package:firebase_messaging/firebase_messaging.dart’;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
This ensures Firebase is ready before your app starts handling notifications.
Android notification channels Flutter setup requires some platform-specific configuration. Don’t worry – it’s simpler than it sounds.
dependencies {
classpath ‘com.google.gms:google-services:4.3.15’
}
apply plugin: ‘com.google.gms.google-services’
android {
compileSdkVersion 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
}
}
Create notification channels for Android 8.0+ compatibility:
import ‘package:flutter_local_notifications/flutter_local_notifications.dart’;
class NotificationService {
static final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
static Future<void> initialize() async {
const AndroidInitializationSettings androidSettings =
AndroidInitializationSettings(‘@drawable/ic_notification’);
const InitializationSettings settings = InitializationSettings(
android: androidSettings,
);
await _notifications.initialize(settings);
// Create notification channel
const AndroidNotificationChannel channel = AndroidNotificationChannel(
‘high_importance_channel’,
‘High Importance Notifications’,
description: ‘This channel is used for important notifications.’,
importance: Importance.high,
);
await _notifications.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);
}
}
This creates a dedicated channel for your high-priority notifications.
iOS notification setup Flutter has its own quirks, but the payoff is worth it.
iOS requires explicit permission for notifications:
class PermissionService {
static Future<bool> requestNotificationPermissions() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
carPlay: false,
criticalAlert: false,
provisional: false,
);
return settings.authorizationStatus == AuthorizationStatus.authorized;
}
}
Add these capabilities in Xcode:
Update ios/Runner/AppDelegate.swift:
import Flutter
import UIKit
import Firebase
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Flutter notification permissions are crucial for user experience. Nobody likes apps that demand permissions without explanation.
class SmartPermissions {
static Future<void> requestWithContext(BuildContext context) async {
// Show explanation dialog first
bool shouldRequest = await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(‘Stay Updated!’),
content: Text(‘Get notified about new features, updates, and special offers.’),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(‘Not Now’),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(‘Enable’),
),
],
);
},
) ?? false;
if (shouldRequest) {
await PermissionService.requestNotificationPermissions();
}
}
}
This approach explains the value before asking for permission, leading to higher acceptance rates.
Foreground notification handling determines how your app responds when it’s actively being used.
class ForegroundNotificationHandler {
static void initialize() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print(‘Received foreground message: ${message.messageId}’);
// Show local notification
_showLocalNotification(message);
// Update UI or navigate
_handleNotificationAction(message);
});
}
static void _showLocalNotification(RemoteMessage message) async {
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
‘high_importance_channel’,
‘High Importance Notifications’,
channelDescription: ‘This channel is used for important notifications.’,
importance: Importance.high,
priority: Priority.high,
icon: ‘@drawable/ic_notification’,
);
const NotificationDetails details = NotificationDetails(
android: androidDetails,
);
await FlutterLocalNotificationsPlugin().show(
message.hashCode,
message.notification?.title,
message.notification?.body,
details,
payload: message.data.toString(),
);
}
}
This ensures users see notifications even when your app is open.
Flutter background notifications work differently than foreground notifications. The system handles delivery, but you control the response.
@pragma(‘vm:entry-point’)
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print(‘Handling background message: ${message.messageId}’);
// Process data or update local storage
await _processBackgroundData(message);
}
Future<void> _processBackgroundData(RemoteMessage message) async {
// Example: Update local database
if (message.data.containsKey(‘user_action’)) {
await LocalStorage.updateUserAction(message.data[‘user_action’]);
}
}
// Register the handler in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
class NotificationLaunchHandler {
static Future<void> handleInitialMessage() async {
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
_handleMessageNavigation(initialMessage);
}
}
static void _handleMessageNavigation(RemoteMessage message) {
// Navigate to specific screen based on notification data
if (message.data[‘screen’] == ‘product_detail’) {
NavigationService.navigateToProduct(message.data[‘product_id’]);
}
}
}
Flutter notification icons customization can significantly boost user engagement. A well-designed notification stands out in a crowded notification panel.
class NotificationStyling {
static const AndroidNotificationDetails richNotification = AndroidNotificationDetails(
‘rich_channel’,
‘Rich Notifications’,
channelDescription: ‘Styled notifications with images and actions’,
importance: Importance.high,
priority: Priority.high,
icon: ‘@drawable/custom_icon’,
color: Color(0xFF2196F3),
ledColor: Color(0xFF2196F3),
ledOnMs: 1000,
ledOffMs: 500,
enableVibration: true,
vibrationPattern: Int64List.fromList([0, 1000, 500, 1000]),
groupKey: ‘app_notifications’,
setAsGroupSummary: true,
);
static Future<void> showStyledNotification(String title, String body) async {
const NotificationDetails details = NotificationDetails(
android: richNotification,
);
await FlutterLocalNotificationsPlugin().show(
DateTime.now().millisecondsSinceEpoch ~/ 1000,
title,
body,
details,
);
}
}
class BadgeManager {
static Future<void> updateBadgeCount(int count) async {
await FirebaseMessaging.instance.setAutoInitEnabled(true);
// Badge count is handled automatically by FCM on iOS
// For custom logic, use flutter_local_notifications
}
static Future<void> clearBadge() async {
await FlutterLocalNotificationsPlugin().cancelAll();
}
}
Deep linking with notifications transforms simple alerts into powerful user journey triggers.
class DeepLinkHandler {
static void initialize() {
// Handle notification taps when app is running
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_processDeepLink(message.data);
});
// Handle app launch from notification
_handleInitialMessage();
}
static void _processDeepLink(Map<String, dynamic> data) {
String? route = data[‘route’];
String? productId = data[‘product_id’];
String? userId = data[‘user_id’];
switch (route) {
case ‘product’:
NavigationService.navigateToProduct(productId);
break;
case ‘profile’:
NavigationService.navigateToProfile(userId);
break;
case ‘chat’:
NavigationService.navigateToChat(data[‘chat_id’]);
break;
default:
NavigationService.navigateToHome();
}
}
static Future<void> _handleInitialMessage() async {
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
_processDeepLink(initialMessage.data);
}
}
}
class NavigationService {
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
static void navigateToProduct(String? productId) {
if (productId != null) {
navigatorKey.currentState?.pushNamed(‘/product’, arguments: {‘id’: productId});
}
}
static void navigateToProfile(String? userId) {
if (userId != null) {
navigatorKey.currentState?.pushNamed(‘/profile’, arguments: {‘userId’: userId});
}
}
}
Grouping notifications in Flutter prevents notification spam and improves user experience.
class NotificationGrouping {
static const String GROUP_KEY = ‘app_notifications’;
static int notificationId = 0;
static Future<void> showGroupedNotification(
String title,
String body,
String category
) async {
notificationId++;
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
‘grouped_channel’,
‘Grouped Notifications’,
channelDescription: ‘Notifications that can be grouped together’,
importance: Importance.high,
priority: Priority.high,
groupKey: GROUP_KEY,
icon: ‘@drawable/ic_notification’,
);
const NotificationDetails details = NotificationDetails(
android: androidDetails,
);
// Show individual notification
await FlutterLocalNotificationsPlugin().show(
notificationId,
title,
body,
details,
);
// Show group summary if needed
if (notificationId > 1) {
await _showGroupSummary(category);
}
}
static Future<void> _showGroupSummary(String category) async {
const AndroidNotificationDetails summaryDetails = AndroidNotificationDetails(
‘grouped_channel’,
‘Grouped Notifications’,
channelDescription: ‘Summary of grouped notifications’,
importance: Importance.high,
priority: Priority.high,
groupKey: GROUP_KEY,
setAsGroupSummary: true,
icon: ‘@drawable/ic_notification’,
);
const NotificationDetails summaryNotificationDetails = NotificationDetails(
android: summaryDetails,
);
await FlutterLocalNotificationsPlugin().show(
0, // Use 0 for summary
‘$category Updates’,
‘You have $notificationId new notifications’,
summaryNotificationDetails,
);
}
}
Notification analytics Firebase helps you understand what works and what doesn’t.
import ‘package:firebase_analytics/firebase_analytics.dart’;
class NotificationAnalytics {
static final FirebaseAnalytics _analytics = FirebaseAnalytics.instance;
static Future<void> trackNotificationReceived(RemoteMessage message) async {
await _analytics.logEvent(
name: ‘notification_received’,
parameters: {
‘message_id’: message.messageId ?? ‘unknown’,
‘title’: message.notification?.title ?? ‘no_title’,
‘campaign’: message.data[‘campaign’] ?? ‘default’,
‘timestamp’: DateTime.now().millisecondsSinceEpoch,
},
);
}
static Future<void> trackNotificationOpened(RemoteMessage message) async {
await _analytics.logEvent(
name: ‘notification_opened’,
parameters: {
‘message_id’: message.messageId ?? ‘unknown’,
‘action’: message.data[‘action’] ?? ‘open_app’,
‘screen’: message.data[‘screen’] ?? ‘home’,
},
);
}
static Future<void> trackNotificationDismissed(String messageId) async {
await _analytics.logEvent(
name: ‘notification_dismissed’,
parameters: {
‘message_id’: messageId,
‘timestamp’: DateTime.now().millisecondsSinceEpoch,
},
);
}
}
class NotificationPerformance {
static Future<void> measureDeliveryTime(RemoteMessage message) async {
int sentTime = int.tryParse(message.data[‘sent_timestamp’] ?? ‘0’) ?? 0;
int receivedTime = DateTime.now().millisecondsSinceEpoch;
int deliveryTime = receivedTime – sentTime;
await FirebaseAnalytics.instance.logEvent(
name: ‘notification_delivery_time’,
parameters: {
‘delivery_time_ms’: deliveryTime,
‘message_type’: message.data[‘type’] ?? ‘general’,
},
);
}
}
Testing is crucial for ensuring your notifications work flawlessly across different scenarios.
class TokenManager {
static Future<String?> getToken() async {
try {
String? token = await FirebaseMessaging.instance.getToken();
print(‘FCM Token: $token’);
return token;
} catch (e) {
print(‘Error getting FCM token: $e’);
return null;
}
}
static void listenToTokenChanges() {
FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
print(‘Token refreshed: $token’);
// Send updated token to your server
_sendTokenToServer(token);
});
}
static Future<void> _sendTokenToServer(String token) async {
// Implement your server communication logic
// This is crucial for maintaining accurate user targeting
}
}
When it comes to implementing complex features like Flutter Firebase push notifications, having experienced developers makes all the difference.
FBIP brings years of Flutter development expertise to the table, specializing in creating robust, scalable mobile applications that keep users engaged.
Unlike generic development shops, FBIP understands the nuances of Firebase integration and mobile user experience. Their team has implemented push notification systems for e-commerce apps, social platforms, and business applications across various industries.
What sets FBIP apart is their end-to-end approach. They don’t just code the technical implementation – they help you design notification strategies that actually convert users and drive business results.
Their portfolio includes successful Flutter apps with sophisticated notification systems that handle everything from real-time chat messages to complex e-commerce workflows.
Whether you’re building your first Flutter app or scaling an existing one, FBIP’s expertise in Firebase integration, performance optimization, and user experience design ensures your push notification system works flawlessly from day one.
Their clients consistently praise their ability to deliver complex features on time while maintaining clean, maintainable code that scales with business growth.
Implementing Flutter Firebase push notifications transforms your app from a static tool into a dynamic, engaging platform that keeps users coming back.
We’ve covered everything from basic FCM integration to advanced features like deep linking, notification grouping, and analytics tracking.
The key to success lies in thoughtful implementation – requesting permissions gracefully, crafting relevant messages, and providing seamless user experiences across foreground, background, and terminated states.
Remember to test thoroughly on both Android and iOS devices, monitor your notification performance through Firebase Analytics, and continuously optimize based on user engagement metrics.
Start with the basics, then gradually implement advanced features as your app grows. Your users will appreciate the thoughtful notifications that add real value to their experience.
Ready to implement Flutter Firebase push notifications that actually engage your users? The techniques in this guide provide a solid foundation for building notification systems that drive results.
Ready to build Flutter apps with powerful push notification systems? Contact FBIP for expert Flutter development services that turn your mobile app ideas into engaging, notification-driven user experiences.
Q: Why aren’t my push notifications working on iOS simulators?
Push notifications require physical devices with valid provisioning profiles and APNs certificates. iOS simulators don’t support push notifications, so always test on real devices during development.
Q: How do I handle notification permissions that were previously denied?
Once denied, you can’t programmatically re-request permissions. Direct users to device settings or show in-app explanations about enabling notifications manually through Settings → Your App → Notifications.
Q: What’s the difference between data and notification messages in Firebase?
Notification messages display automatically when the app is in background, while data messages always trigger your app code. Use data messages for custom handling and notification messages for simple alerts.
Q: How can I schedule local notifications instead of server-sent ones?
Use the flutter_local_notifications plugin with scheduling features. Create notifications locally based on user actions, time zones, or app events without requiring server infrastructure.
Q: Why do my background notifications sometimes not appear immediately?
Android and iOS implement battery optimization and doze modes that can delay notification delivery. This is normal behavior designed to preserve battery life and improve user experience.
FBIP, a leading brand in the field of IT solutions provider have been successfully delivering excellent services to their esteemed clients across the globe for more than 3 years
© 2018 FBIP. All rights are reserved.
WhatsApp us