Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably send messages and notifications at no cost. In Flutter, FCM is widely used to implement push notifications, allowing you to engage users even when they’re not actively using your app. This blog post provides a comprehensive guide on integrating FCM into your Flutter app to send and receive push notifications.
What is Firebase Cloud Messaging (FCM)?
Firebase Cloud Messaging (FCM) is a service that enables you to send notifications and data from servers to mobile devices and vice versa. FCM is built for scale and provides a unified way to send push notifications across various platforms such as Android, iOS, and web apps. With FCM, you can send two types of messages: notification messages and data messages.
Why Use FCM in Flutter?
- User Engagement: Keeps users informed and engaged with timely notifications.
- Cross-Platform: Supports sending messages to Android, iOS, and web apps from a single platform.
- Reliable Delivery: FCM ensures reliable message delivery even when the app is in the background or terminated.
- Cost-Effective: FCM is free of charge, making it an ideal choice for sending push notifications.
How to Integrate FCM in a Flutter App
Follow these steps to integrate FCM into your Flutter app:
Step 1: Set Up Firebase Project
First, you need to create a Firebase project in the Firebase Console.
- Go to the Firebase Console.
- Click on “Add project.”
- Enter your project name, and follow the prompts to configure your project.
Step 2: Add Firebase to Your Flutter App
2.1: Android Setup
- Register your Android app by adding the app’s package name to your Firebase project.
- Download the
google-services.json
file and place it in theandroid/app/
directory of your Flutter project. - Add the Firebase SDK to your project. In your project-level
build.gradle
(android/build.gradle
), add the following dependency:
dependencies {
classpath "com.google.gms:google-services:4.3.15" // Make sure to use the latest version
}
- In your app-level
build.gradle
(android/app/build.gradle
), add the following plugins and dependencies:
plugins {
id "com.android.application"
id "org.jetbrains.kotlin.android"
id "com.google.gms.google-services"
}
dependencies {
implementation platform('com.google.firebase:firebase-bom:32.7.2') // Use the latest version
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-messaging-ktx'
}
- Sync your Gradle files.
2.2: iOS Setup
- Register your iOS app by adding the app’s bundle ID to your Firebase project.
- Download the
GoogleService-Info.plist
file and add it to the root of your Xcode project. - Add the Firebase SDK to your project using CocoaPods. Create or open the
Podfile
in theios/
directory of your Flutter project and add the following lines:
platform :ios, '11.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
target 'Runner' do
use_frameworks!
pods_for_target 'Runner'
pod 'FirebaseCore'
pod 'FirebaseMessaging'
end
- Run
pod install
in theios/
directory. - In your Xcode project, enable push notifications:
- Go to your project settings.
- Select “Signing & Capabilities.”
- Add the “Push Notifications” capability.
- Configure background modes:
- Select “Signing & Capabilities.”
- Add the “Background Modes” capability and check “Remote notifications.”
Step 3: Add the Firebase Messaging Plugin
In your Flutter project, add the firebase_messaging
plugin to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.24.2
firebase_messaging: ^14.9.0
Run flutter pub get
to install the plugin.
Step 4: Initialize Firebase
In your main.dart
file, initialize Firebase before using any Firebase services:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('FCM Example'),
),
body: Center(
child: Text('Firebase Messaging Example'),
),
),
);
}
}
Step 5: Request Notification Permissions
To receive notifications on iOS, you need to request permissions from the user:
import 'package:firebase_messaging/firebase_messaging.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
runApp(MyApp());
}
Step 6: Get the Device Token
To send notifications to a specific device, you need to obtain the device token:
import 'package:firebase_messaging/firebase_messaging.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging messaging = FirebaseMessaging.instance;
String? token = await messaging.getToken();
print('Firebase token: $token');
runApp(MyApp());
}
Save this token to your server or use it to send test notifications.
Step 7: Handle Incoming Messages
You can handle incoming messages in three scenarios: foreground, background, and terminated state.
7.1: Foreground State
To handle messages when the app is in the foreground, use FirebaseMessaging.onMessage
:
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
runApp(MyApp());
}
7.2: Background State
To handle messages when the app is in the background or terminated, use FirebaseMessaging.onBackgroundMessage
. This handler must be a top-level function:
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}");
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
7.3: Terminated State
You can also handle the initial message when the app is launched from a terminated state:
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
// Handle the message here
print('Initial message: ${initialMessage.notification}');
}
runApp(MyApp());
}
Step 8: Sending a Test Notification
You can send test notifications via the Firebase Console:
- Go to the Firebase Console.
- Select your project.
- Navigate to “Cloud Messaging” under the “Engage” section.
- Click on “Send your first message.”
- Fill in the notification details, such as title and body.
- In the “Send message” section, choose “Send to device” and enter your device token.
- Click “Send.”
Complete Example
Here’s a complete example combining all the steps:
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}");
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
String? token = await messaging.getToken();
print('Firebase token: $token');
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
print('Initial message: ${initialMessage.notification}');
}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('FCM Example'),
),
body: Center(
child: Text('Firebase Messaging Example'),
),
),
);
}
}
Conclusion
Integrating Firebase Cloud Messaging (FCM) into your Flutter app enhances user engagement and ensures timely delivery of important notifications. By following the steps outlined in this blog post, you can successfully set up FCM, handle incoming messages in various app states, and send test notifications. Implementing FCM in your Flutter app enables you to keep your users informed and connected, thereby improving the overall app experience.