In the world of mobile app development, push notifications are essential for engaging users and keeping them informed about important updates, messages, or events. Firebase Cloud Messaging (FCM) provides a reliable and scalable solution for implementing push notifications in Flutter apps. This comprehensive guide will walk you through setting up FCM and integrating it into your Flutter project.
What is Firebase Cloud Messaging (FCM)?
Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably deliver messages and notifications at no cost. FCM allows you to send notifications, transfer messages, and perform various types of mobile communications between your server and client apps.
Why Use FCM for Push Notifications?
- Reliability: FCM provides reliable message delivery, ensuring notifications reach your users.
- Scalability: It’s designed to scale to millions of users without compromising performance.
- Cost-Effective: FCM is available at no cost, making it a great choice for projects of all sizes.
- Cross-Platform Support: FCM supports Android, iOS, and web platforms.
- Rich Features: Offers features like message targeting, analytics, and advanced options for customizing notification behavior.
Prerequisites
Before you begin, make sure you have the following:
- A Flutter development environment set up (Flutter SDK, IDE like VS Code or Android Studio).
- A Firebase project created on the Firebase Console (console.firebase.google.com).
- A basic understanding of Dart and Flutter.
Step-by-Step Implementation
Step 1: Set Up Firebase Project
First, you need to create a new project in the Firebase Console or use an existing one. Follow these steps:
- Go to the Firebase Console.
- Click “Add project” and follow the instructions.
- Once your project is created, navigate to “Project settings.”
Step 2: Add Flutter App to Firebase Project
Now, add your Flutter app to the Firebase project. Follow these steps:
For Android:
- In the Firebase Console, click the Android icon.
- Enter your app’s package name (found in
android/app/build.gradle
underdefaultConfig
). - Download the
google-services.json
file and place it in yourandroid/app/
directory. - Add the Firebase SDK to your project:
- In your project-level
build.gradle
file (android/build.gradle
):dependencies { classpath("com.google.gms:google-services:4.4.0") // check for latest version }
- In your app-level
build.gradle
file (android/app/build.gradle
):apply plugin: 'com.google.gms.google-services' dependencies { implementation platform('com.google.firebase:firebase-bom:32.8.0') // Check for the latest version implementation 'com.google.firebase:firebase-analytics-ktx' implementation 'com.google.firebase:firebase-messaging-ktx' }
- In your project-level
- Sync your Gradle files to apply the changes.
For iOS:
- In the Firebase Console, click the iOS icon.
- Enter your app’s Bundle ID (found in Xcode under your project target’s General tab).
- Download the
GoogleService-Info.plist
file and add it to your Xcode project. - Add the Firebase SDK to your project via CocoaPods.
- Create or update your
Podfile
:platform :ios, '11.0' # ensure min ios version is configured target 'Runner' do use_frameworks! pod 'Firebase/Core' pod 'Firebase/Messaging' end
- Run
pod install
in your terminal from theios
directory.
- Create or update your
- Open your Xcode project’s
ios/Runner/AppDelegate.swift
(orAppDelegate.m
) and add the following:Swift:
import FirebaseCore import FirebaseMessaging @UIApplicationMain class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { FirebaseApp.configure() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
Objective-C:
@import Firebase; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [FIRApp configure]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; }
- For Receiving Notifications you need to enable the Push Notifications capability on your Xcode project. Also ensure that you have correctly configured your APNs certificates in the Apple Developer Portal and uploaded to Firebase Cloud Messaging settings page.
Step 3: Add Firebase Messaging Plugin to Flutter
Add the firebase_messaging
plugin to your Flutter project. This plugin allows your Flutter app to receive and handle FCM messages.
dependencies:
firebase_core: ^2.31.0 # Check latest version
firebase_messaging: ^14.6.0 # Check latest version
Run flutter pub get
in your terminal to install the plugin.
Step 4: Initialize Firebase in Flutter
Initialize Firebase in your Flutter app’s main function:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
Step 5: Request Notification Permissions
Request permission from the user to send notifications:
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
class _MyAppState extends State {
@override
void initState() {
super.initState();
requestPermission();
}
void requestPermission() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission: ${settings.authorizationStatus}');
} else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
print('User granted provisional permission');
} else {
print('User declined or has not accepted permission');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter FCM Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('FCM Example'),
),
body: Center(
child: Text('Welcome to Flutter FCM'),
),
),
);
}
}
Step 6: Get the FCM Token
Obtain the FCM token for the device, which you’ll need to send targeted notifications. Store this token on your server:
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
class _MyAppState extends State {
@override
void initState() {
super.initState();
getToken();
requestPermission();
}
String? token;
void getToken() async {
await FirebaseMessaging.instance.getToken().then((value){
setState(() {
token = value;
});
print('FCM Token: $token'); // Log the token or send to server
});
}
void requestPermission() async {
//Permission logic ...
}
@override
Widget build(BuildContext context) {
//Build widget
}
}
Step 7: Handle Incoming Messages
Implement message handling for when the app is in the foreground, background, or terminated:
Foreground Messages:
@override
void initState() {
super.initState();
getToken();
requestPermission();
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Handling a foreground message: ${message.messageId}');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification?.body}');
}
// Handle the message (e.g., display a dialog, update UI)
});
}
Background and Terminated Messages:
Define a top-level function to handle background messages. This function must be outside any class. For isolate support, Firebase requires this as a top-level function
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
print('Message data: ${message.data}');
// Handle background message (e.g., show notification, update local data)
}
Then assign this to the onBackgroundMessage
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Step 8: Send a Test Notification
You can send test notifications from the Firebase Console:
- Go to the Firebase Console.
- Select “Cloud Messaging.”
- Click “Send your first message.”
- Fill in the required fields (notification title, text, etc.).
- Send the notification to the device using the FCM token.
Code Example: Full Integration
Here’s the complete example of implementing Firebase Cloud Messaging in a Flutter app:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
print('Message data: ${message.data}');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
String? token;
@override
void initState() {
super.initState();
getToken();
requestPermission();
// Foreground message handling
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Handling a foreground message: ${message.messageId}');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification?.body}');
// Show the notification using FlutterLocalNotificationsPlugin if needed.
}
});
}
void getToken() async {
await FirebaseMessaging.instance.getToken().then((value){
setState(() {
token = value;
});
print('FCM Token: $token'); // Log the token or send to server
});
}
void requestPermission() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission: ${settings.authorizationStatus}');
} else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
print('User granted provisional permission');
} else {
print('User declined or has not accepted permission');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter FCM Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('FCM Example'),
),
body: Center(
child: Text('Welcome to Flutter FCM'),
),
),
);
}
}
Troubleshooting
- Notifications Not Received:
- Verify FCM token validity.
- Check network connectivity.
- Ensure the correct
google-services.json
orGoogleService-Info.plist
file is configured. - Check if background message handling is implemented correctly.
- On iOS, ensure that APNs certificates are correctly configured in the Apple Developer Portal and uploaded to Firebase Cloud Messaging settings page, and that push notifications capability is enabled in Xcode
- Android Notifications Delayed:
- Some devices might have battery optimization settings that delay notifications.
- iOS Notifications Not Displayed:
- Verify APNs certificates.
- Ensure push notification settings are enabled for your app in iOS settings.
Conclusion
Integrating Firebase Cloud Messaging (FCM) into your Flutter app enables you to send reliable and timely push notifications. By following this comprehensive guide, you can effectively set up FCM, handle incoming messages, and keep your users engaged. Leveraging push notifications will help improve user retention, provide timely updates, and enhance the overall user experience of your Flutter applications.