Using Firebase Cloud Messaging (FCM) in Flutter

Firebase Cloud Messaging (FCM) is a powerful and versatile service provided by Google that enables you to reliably send and receive notifications and messages on Android, iOS, and web platforms at no cost. In Flutter, FCM can be integrated to deliver push notifications, communicate with app users, and even handle data synchronization. This guide provides a comprehensive walkthrough of implementing FCM in a Flutter application.

What is Firebase Cloud Messaging (FCM)?

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 email or other data is available to sync. You can send notifications to drive user re-engagement and retention. For use cases such as instant messaging, a message can transfer a payload of up to 4KB to a client app. FCM offers high reliability and scalability, making it an essential tool for mobile app developers.

Why Use Firebase Cloud Messaging (FCM)?

  • Cross-Platform Support: Works seamlessly with Android, iOS, and web applications.
  • Reliability: Ensures high reliability in message delivery.
  • Scalability: Supports sending messages to a large number of devices.
  • Cost-Effective: FCM is offered at no cost for most use cases.
  • Versatile: Supports different types of messages, including notifications and data messages.

How to Implement Firebase Cloud Messaging (FCM) in Flutter

Integrating FCM into a Flutter application involves several steps, including setting up Firebase, configuring your Flutter project, and writing the code to handle messages.

Step 1: Set Up Firebase Project

Before you begin, you need a Firebase project. If you don’t have one, follow these steps:

  1. Go to the Firebase Console.
  2. Click on “Add project”.
  3. Enter your project name, and follow the prompts to complete the setup.

Step 2: Add Firebase to Your Flutter App

Next, add Firebase to your Flutter app using the Firebase CLI:

  1. Install the Firebase CLI:
  2. npm install -g firebase-tools
  3. Log in to Firebase:
  4. firebase login
  5. Navigate to your Flutter project directory in the terminal.
  6. Run the following command:
  7. flutterfire configure
  8. Select your Firebase project and follow the instructions to configure your app.

Step 3: Add Firebase Dependencies

Add the necessary Firebase 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 4: Initialize Firebase

Initialize Firebase in your Flutter app. Update your main.dart file to include the following:

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 Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FCM Demo'),
      ),
      body: Center(
        child: Text('Push Notification Demo'),
      ),
    );
  }
}

Step 5: Get FCM Token

Request an FCM token for the device. This token is used to send push notifications to a specific device. Add the following code to your MyHomePage widget:

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

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

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

  Future<void> getToken() async {
    String? token = await FirebaseMessaging.instance.getToken();
    print('FCM Token: $token');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FCM Demo'),
      ),
      body: Center(
        child: Text('Push Notification Demo'),
      ),
    );
  }
}

The FCM token will be printed to the console when the app runs.

Step 6: Handle Incoming Messages

Handle incoming messages using the FirebaseMessaging class. Configure the app to listen for incoming notifications while the app is in the foreground, background, or terminated state.

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

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

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

  Future<void> getToken() async {
    String? token = await FirebaseMessaging.instance.getToken();
    print('FCM Token: $token');
  }

  void configureMessaging() {
    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}');
        // Show a dialog or a snackbar to display the notification
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text(message.notification!.title ?? 'Notification'),
              content: Text(message.notification!.body ?? 'You have a new message!'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  child: Text('OK'),
                ),
              ],
            );
          },
        );
      }
    });

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print('Message opened app: ${message.notification}');
      // Handle the message when the app is opened from a background state
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => SecondScreen(message: message)),
      );
    });

    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FCM Demo'),
      ),
      body: Center(
        child: Text('Push Notification Demo'),
      ),
    );
  }
}

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  print("Handling a background message: ${message.messageId}");
  // Handle the message when the app is in the background
}

class SecondScreen extends StatelessWidget {
  final RemoteMessage message;

  SecondScreen({required this.message});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: Center(
        child: Text('Received notification: ${message.notification?.body}'),
      ),
    );
  }
}

Explanation of the code:

  • FirebaseMessaging.onMessage.listen: Handles messages when the app is in the foreground.
  • FirebaseMessaging.onMessageOpenedApp.listen: Handles messages when the user taps on a notification and the app is opened from the background.
  • FirebaseMessaging.onBackgroundMessage: Handles messages when the app is in the background or terminated. This requires the function to be a top-level function annotated with @pragma('vm:entry-point').

Step 7: Send Test Notification

You can send test notifications using the Firebase Console:

  1. Go to the Firebase Console.
  2. Navigate to “Cloud Messaging” under the “Engage” section.
  3. Click on “Send your first message”.
  4. Enter the notification title and text.
  5. Choose the target (e.g., app instance, topic).
  6. Send the notification.

Best Practices for Using FCM

  • Handle Token Refresh: Implement logic to handle token refresh (FirebaseMessaging.instance.onTokenRefresh).
  • Use Data Messages: For non-notification use cases, use data messages to send custom payloads.
  • Monitor Performance: Track FCM performance in the Firebase Console to identify and resolve issues.
  • Handle Different Message Types: Properly handle foreground, background, and terminated states to ensure messages are processed correctly.

Conclusion

Firebase Cloud Messaging (FCM) is a crucial tool for mobile app developers to deliver timely and relevant content to users. By integrating FCM into your Flutter applications, you can enhance user engagement, improve retention, and provide a seamless communication experience. This guide provides a detailed walkthrough of implementing FCM, from setting up Firebase to handling incoming messages and implementing best practices. Follow these steps to effectively integrate FCM into your Flutter projects.