Using Firebase Cloud Messaging (FCM) to Send and Receive Notifications in Flutter

Firebase Cloud Messaging (FCM) is a powerful cross-platform messaging solution that lets you reliably send and receive notifications and messages. In Flutter, integrating FCM can enhance user engagement by enabling you to send targeted notifications based on user behavior, preferences, or real-time updates. This blog post guides you through setting up and using Firebase Cloud Messaging in your Flutter applications.

What is Firebase Cloud Messaging (FCM)?

Firebase Cloud Messaging (FCM) is a service that allows you to send notifications, messages, and data from your server to your users’ mobile devices or web applications. FCM handles the reliability and scalability of sending messages, and it works on both Android and iOS platforms. FCM simplifies sending messages with its robust and straightforward architecture.

Why Use FCM in Flutter?

  • Cross-Platform Support: Send messages to both Android and iOS devices.
  • Reliable Delivery: FCM ensures high delivery rates and provides message retries.
  • Targeted Messaging: Send messages to specific users or groups based on interests or behavior.
  • Ease of Integration: Firebase provides comprehensive tools and libraries for integrating FCM into Flutter apps.

How to Integrate Firebase Cloud Messaging (FCM) into a Flutter App

Here’s a comprehensive guide to implementing FCM in a Flutter application.

Step 1: Create a Firebase Project

First, create a new project in the Firebase console:

  1. Go to the Firebase Console.
  2. Click “Add project.”
  3. Enter your project name and follow the setup instructions.
  4. Once the project is created, select it to proceed.

Step 2: Add Firebase to Your Flutter App

Add Firebase to your Flutter app by following these steps:

  1. In the Firebase console, click the iOS or Android icon to add Firebase to your respective app.
  2. Follow the instructions to register your app with Firebase. You’ll need to:
    • Enter your app’s package name (Android) or bundle ID (iOS).
    • Download the google-services.json (Android) or GoogleService-Info.plist (iOS) file.
    • Place the configuration file in the appropriate directory:
      • For Android: Place google-services.json in the android/app directory.
      • For iOS: Drag and drop GoogleService-Info.plist into your Flutter project in Xcode.

Step 3: Configure Android Project

Update your Android project to use the Google Services plugin:

  1. In the android/build.gradle file, add the following dependency:
dependencies {
    classpath 'com.google.gms:google-services:4.3.10'
}
  1. In the android/app/build.gradle file, apply the plugin at the top:
apply plugin: 'com.google.gms.google-services'

Step 4: Add Firebase Core and FCM Dependencies

Add the required dependencies to your pubspec.yaml file:

dependencies:
  firebase_core: ^2.15.0
  firebase_messaging: ^14.6.0

Run flutter pub get to install the dependencies.

Step 5: Initialize Firebase in Flutter

Initialize Firebase in your Flutter app within the main function:

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(
      title: 'FCM Example',
      home: MyHomePage(),
    );
  }
}

Step 6: Configure Firebase Messaging

Configure Firebase Messaging by requesting permissions and handling incoming messages.

Requesting Notification Permissions

Request notification permissions from the user:

import 'package:firebase_messaging/firebase_messaging.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    requestPermissions();
  }

  Future<void> requestPermissions() 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');
    } 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 Scaffold(
      appBar: AppBar(
        title: Text('FCM Example'),
      ),
      body: Center(
        child: Text('Firebase Cloud Messaging Example'),
      ),
    );
  }
}
Handling Incoming Messages

Handle incoming messages while the app is in the foreground using FirebaseMessaging.onMessage:

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Set up local notifications
  const AndroidInitializationSettings initializationSettingsAndroid =
      AndroidInitializationSettings('app_icon'); // Replace with your app icon
  final InitializationSettings initializationSettings =
      InitializationSettings(android: initializationSettingsAndroid);
  await flutterLocalNotificationsPlugin.initialize(initializationSettings);

  // Handle incoming messages in the foreground
  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}');
      // Display the notification using flutter_local_notifications
      displayNotification(message);
    }
  });

  runApp(MyApp());
}

Future<void> displayNotification(RemoteMessage message) async {
  const AndroidNotificationDetails androidNotificationDetails =
      AndroidNotificationDetails(
    'your_channel_id',
    'Your Channel Name',
    channelDescription: 'Channel description',
    importance: Importance.max,
    priority: Priority.high,
    ticker: 'ticker',
  );
  const NotificationDetails platformChannelSpecifics =
      NotificationDetails(android: androidNotificationDetails);
  await flutterLocalNotificationsPlugin.show(
    0, // Notification ID
    message.notification?.title,
    message.notification?.body,
    platformChannelSpecifics,
    payload: 'item x',
  );
}
Handling Background Messages

To handle messages when the app is in the background or terminated, use the FirebaseMessaging.onBackgroundMessage handler:

import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';

@pragma('vm:entry-point')
Future<void> _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());
}

Note: The _firebaseMessagingBackgroundHandler function must be a top-level function (not inside a class) and marked with @pragma('vm:entry-point') to be accessible in the background.

Step 7: Get the FCM Token

Retrieve the FCM token for the device:

FirebaseMessaging messaging = FirebaseMessaging.instance;

messaging.getToken().then((token) {
  print("FCM Token: $token");
  // Send this token to your server to target specific devices
});

Step 8: Send a Test Notification

To test your setup, send a notification from the Firebase console:

  1. Go to the Firebase Console, select your project, and navigate to Cloud Messaging.
  2. Click “Send your first message.”
  3. Enter the notification title and text.
  4. Choose “Send test message” and enter the FCM token obtained in the previous step.
  5. Click “Test.”

Conclusion

Firebase Cloud Messaging (FCM) provides a comprehensive solution for sending and receiving notifications in Flutter applications. By following this guide, you can integrate FCM into your Flutter apps to improve user engagement and deliver timely updates. Whether it’s for real-time messaging, targeted notifications, or background data synchronization, FCM is a powerful tool for enhancing your app’s functionality and user experience.