Flutter Firebase Cloud Messaging (FCM) Updated

Amanullah Bahram
4 min readOct 26, 2024

--

Firebase Cloud Messaging (FCM) is a service that allows you to send notifications and messages to devices running your app. This tutorial will guide you through the setup and usage of FCM in Flutter to receive notifications when the app is in both the foreground and background.

We’ll go through:

  1. Setting up Firebase for your Flutter project.
  2. Integrating Firebase Cloud Messaging (FCM) into your Flutter app.
  3. Sending and receiving push notifications in both foreground and background states.

Prerequisites

  • Firebase Configured Watch Here if you don’t know how
  • A real device to test

1. Add Firebase SDK to Flutter

To use Firebase with Flutter, you need to add Firebase SDK dependencies to your project.

Open pubspec.yaml and add the following dependencies:

dependencies:
flutter:
sdk: flutter
firebase_core: ^3.6.0
firebase_messaging: ^15.1.3
flutter_local_notifications: ^17.2.3

Install the dependencies:

flutter pub get

2. Initialize Firebase in Flutter

In the lib/main.dart, initialize Firebase at the start of your app.

import 'package:fcm_test/services/notification_service.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await NotificationService.instance.initialize();
runApp(const MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Text('hello'),
),
),
);
}
}

3. Configure Firebase Messaging

In the services/notification_service.dart, copy this:

1. Background Message Handler

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await NotificationService.instance.setupFlutterNotifications();
await NotificationService.instance.showNotification(message);
}
  • Purpose: This background handler processes notifications when the app is terminated or in the background.
  • @pragma(‘vm’): Ensures that the function is available for background processing by the Dart VM, which is required for FCM background messages.
  • Action: It calls setupFlutterNotifications to initialize notifications and showNotification to display the incoming message.

2. NotificationService Class Singleton

class NotificationService {
NotificationService._();
static final NotificationService instance = NotificationService._();
...
}
  • Purpose: This class is a singleton, meaning only one instance exists throughout the app.
  • Structure: Using the private constructor (NotificationService._()), we create an instance (NotificationService.instance), which can be accessed anywhere in the app.

3. Firebase Messaging and Local Notifications Plugin

final _messaging = FirebaseMessaging.instance;
final _localNotifications = FlutterLocalNotificationsPlugin();
  • These variables hold instances of FirebaseMessaging (to manage FCM) and FlutterLocalNotificationsPlugin (for local notifications).
  • _messaging: Manages FCM tasks like getting tokens and handling messages.
  • _localNotifications: Manages local notifications, which we use to display custom notifications on both Android and iOS.

4. Initialization Method

Future<void> initialize() async {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await _requestPermission();
await _setupMessageHandlers();
final token = await _messaging.getToken();
print('FCM Token: $token');
}
  • FirebaseMessaging.onBackgroundMessage: Registers the background handler to process messages when the app is not active.
  • _requestPermission: Requests notification permissions from the user (iOS specific).
  • _setupMessageHandlers: Sets up message handlers for handling notifications in different states.
  • Get FCM Token: Retrieves the device’s FCM token, which is required to target notifications to specific devices.

5. Permission Request Method

Future<void> _requestPermission() async {
final settings = await _messaging.requestPermission(
alert: true,
badge: true,
sound: true,
provisional: false,
);
print('Permission status: ${settings.authorizationStatus}');
}
  • Purpose: Requests notification permissions from the user, necessary for iOS.
  • Options: Specifies types of notifications allowed (alerts, sounds, badges).
  • Output: Logs the user’s authorization status (granted, denied, or provisional).

6. Setup Flutter Local Notifications

Future<void> setupFlutterNotifications() async {
if (_isFlutterLocalNotificationsInitialized) return;
...
_isFlutterLocalNotificationsInitialized = true;
}
  • Purpose: Initializes local notifications only once.
  • Android Configuration: Sets up an Android notification channel for high-importance notifications, which appear as alerts.
  • iOS Configuration: Sets up iOS-specific notification settings, like alert presentation and sound.
  • Initialization: Calls _localNotifications.initialize to activate notification settings and listeners.

7. Show Notification Method

Future<void> showNotification(RemoteMessage message) async {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
...
}
  • Purpose: Displays a notification with data from an incoming FCM message.
  • Message Processing: Checks if there’s a notification payload and displays it using _localNotifications.show.
  • Android & iOS: Creates notification details with custom settings for both platforms, such as icons and sound, and includes the message data as a payload.

8. Setup Message Handlers

Future<void> _setupMessageHandlers() async {
FirebaseMessaging.onMessage.listen((message) {
showNotification(message);
});
FirebaseMessaging.onMessageOpenedApp.listen(_handleBackgroundMessage);
final initialMessage = await _messaging.getInitialMessage();
if (initialMessage != null) {
_handleBackgroundMessage(initialMessage);
}
}
  • Purpose: Sets up handlers to process notifications in different app states.
  • Foreground Messages: Listens for notifications when the app is active (onMessage), displaying the notification locally.
  • Background & Terminated Messages: Handles notifications opened from the background or terminated state (onMessageOpenedApp and getInitialMessage).

9. Handle Background Message

void _handleBackgroundMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
// open chat screen
}
}
  • Purpose: Processes notifications when the app is launched from a notification.
  • Example Action: If the message contains a specific type (e.g., “chat”), it can navigate to a specific screen or execute other logic.

4. Testing Push Notifications

You can now send test messages to your app using Firebase Cloud Messaging.

  1. Go to the Firebase Console.
  2. Navigate to Cloud Messaging.
  3. Click on New Notification.
  4. Enter the notification details and target the app via its FCM token.
  5. Click Send Message to trigger the push notification.

Make sure your app is either running in the foreground or background to observe the notifications.

Conclusion

In this tutorial, we covered the entire process of integrating Firebase Cloud Messaging into a Flutter app, handling foreground and background notifications, and configuring the required permissions and settings for both Android and iOS. You can now send notifications using Firebase and receive them on your device regardless of whether the app is open or closed.

If you enjoyed this tutorial and want to dive deeper, feel free to check out the GitHub Source complete code for this example.

Happy coding! 🎉

--

--

Amanullah Bahram
Amanullah Bahram

No responses yet