Integrating Firebase Cloud Messaging in Flutter
Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably send messages at no cost. Using FCM, you can notify a client app that new data is available to sync, send instant messages, or deliver promotional offers. Integrating FCM into a Flutter application allows developers to send push notifications to users across Android and iOS platforms seamlessly. This article will guide you through the process of integrating FCM into your Flutter project.
Prerequisites
- A Flutter project set up and ready.
- A Firebase project created in the Firebase Console.
- Basic understanding of Flutter and Dart.
Firebase Project Setup
1. Create a Firebase Project
If you don't already have one, go to the Firebase Console and create a new project. Follow the on-screen instructions.
2. Register Your Android App
- In your Firebase project, click on the Android icon to add an Android app.
- Enter your Android package name (found in `android/app/build.gradle` under `applicationId`).
- Provide an App nickname (optional).
- (Optional) Provide a SHA-1 signing certificate. This is required for Google Sign-In and Dynamic Links but not strictly for FCM.
- Download the `google-services.json` file. Place this file into your Flutter project's `android/app` directory.
- Follow the console instructions to add the Firebase SDK.
Update your `android/build.gradle` (project level) with:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0' // Use your current Android Gradle Plugin version
classpath 'com.google.gms:google-services:4.3.15' // Use the latest google-services plugin version
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10" // Use your current Kotlin version
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Update your `android/app/build.gradle` (app level) by adding these lines:
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services' // Add this line
apply plugin: 'kotlin-android'
android {
// ...
}
dependencies {
// ...
}
3. Register Your iOS App
- In your Firebase project, click on the iOS icon to add an iOS app.
- Enter your iOS bundle ID (found in Xcode under General > Identity > Bundle Identifier).
- Provide an App nickname (optional).
- Download the `GoogleService-Info.plist` file.
- Open your Flutter project in Xcode (Runner.xcworkspace). Drag and drop the `GoogleService-Info.plist` file into the `Runner` folder in Xcode. Ensure "Copy items if needed" is checked.
- Enable Push Notifications and Background Modes (Remote notifications) in Xcode under your target's "Signing & Capabilities" tab.
Flutter Project Setup
1. Add Dependencies
Open your `pubspec.yaml` file and add the `firebase_core` and `firebase_messaging` packages:
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.24.2 # Use the latest version
firebase_messaging: ^14.7.10 # Use the latest version
Run `flutter pub get` to fetch the packages.
2. Initialize Firebase
Before using any Firebase services, you need to initialize Firebase in your `main()` function. It's recommended to do this asynchronously and ensure widgets are initialized.
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FCM Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
String _message = 'Waiting for messages...';
@override
void initState() {
super.initState();
// Initialize FCM listeners here
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Firebase Cloud Messaging'),
),
body: Center(
child: Text(_message),
),
);
}
}
Implementing FCM in Flutter
1. Requesting Permissions (iOS & Web)
For iOS and Web platforms, you need to explicitly request user permission to display notifications. Android handles this automatically.
final 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');
} else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
print('User granted provisional permission');
} else {
print('User declined or has not accepted permission');
}
2. Getting the Device Token
The FCM device token is a unique identifier for each device that your app runs on. You'll need this token to send targeted messages.
String? token = await messaging.getToken();
print("FCM Token: $token");
// Listen for token refresh events
messaging.onTokenRefresh.listen((newToken) {
print("FCM Token refreshed: $newToken");
// If you save tokens to a backend, update it here.
});
3. Handling Foreground Messages
Messages received when the app is in the foreground (open and in use) are handled by `onMessage.listen()`. These messages won't typically show a notification automatically unless explicitly built by your app.
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!.title} / ${message.notification!.body}');
setState(() {
_message = 'Foreground message: ${message.notification!.body}';
});
}
});
4. Handling Background and Terminated Messages
Messages received when the app is in the background or terminated require a top-level `_firebaseMessagingBackgroundHandler` function. This function runs in an isolated scope and must be registered once.
import 'package:firebase_messaging/firebase_messaging.dart';
// Must be a top-level function
@pragma('vm:entry-point')
Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(); // Initialize Firebase again for background isolate
print("Handling a background message: ${message.messageId}");
// You can process the message data here
// For example, update local storage or show a local notification
}
class _MyHomePageState extends State {
// ...
@override
void initState() {
super.initState();
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
if (message != null) {
print('App launched from terminated state by a message: ${message.notification?.body}');
setState(() {
_message = 'Terminated message: ${message.notification?.body}';
});
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('App opened from background by a message: ${message.notification?.body}');
setState(() {
_message = 'Background message: ${message.notification?.body}';
});
// Navigate to a specific screen based on message.data if needed
});
// Register the background message handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// Call permission request and token retrieval logic here
_initializeFCM();
}
Future _initializeFCM() async {
// Request permissions
// Get token
// Listen for foreground messages
// (Existing code for these)
}
// ...
}
Note: For iOS, to enable background message handling, ensure the 'Background processing' capability is enabled in Xcode and add the `FirebaseAppDelegateProxyEnabled` flag to your `Info.plist` and set it to `NO` if you're not using method swizzling.
5. Subscribing to Topics
FCM allows you to send messages to devices that have subscribed to a particular topic. This is useful for sending broadcast messages to user segments.
await FirebaseMessaging.instance.subscribeToTopic('weather');
print('Subscribed to weather topic');
// To unsubscribe:
// await FirebaseMessaging.instance.unsubscribeFromTopic('weather');
Sending Test Messages
1. Using the Firebase Console
Go to your Firebase project, navigate to "Cloud Messaging" from the left menu. Click "Send your first message". You can compose a notification message and send it to specific tokens or topics.
2. Using a REST API
You can also send messages programmatically using the FCM HTTP v1 API. You'll need your server key (from Project settings > Cloud Messaging) or an access token for service account authentication.
// Example payload for sending a message to a topic via HTTP POST to
// https://fcm.googleapis.com/v1/projects//messages:send
// with Authorization: Bearer
{
"message": {
"topic": "weather",
"notification": {
"title": "Weather Update!",
"body": "There's a storm coming to your area. Stay safe!"
},
"data": {
"route": "/weather-alerts",
"alert_type": "severe"
}
}
}
Conclusion
Integrating Firebase Cloud Messaging into your Flutter application provides a robust and scalable solution for sending push notifications across Android and iOS. By following these steps, you can set up Firebase, handle various message states (foreground, background, terminated), and leverage features like topic-based messaging. This enables powerful user engagement strategies, from critical alerts to promotional content, directly within your mobile app.