Flutter: Chat notifications using FCM in a socket.io powered chat app
By: Vikash Kumar
Tags: Flutter, Firebase, Messaging, Socketio, Notifications

A while ago, I wrote an article on how to create a chat app in flutter using a socket.io service. This application connects users to a websocket service and enables real time conversation. While the users can chat when they are online, the application disconnects them from the websocket when they come out of the application. This limitation brings us to the next major requirement in a chat app: receiving chat notifications when the app is running in the background.

Taking a cue from my previous article, here I will add the functionality of receiving chat notifications using FCM (Firebase Cloud Messaging).

Note: In this article, I am covering the changes for ANDROID devices only.

Prerequisites

This article is in continuation to my previous article: Flutter: A chat app in flutter using a Socket.IO service. Kindly follow the steps there before moving forward.

Step#1: Create a new Firebase App

Follow the steps here to create a new Firebase app. Do not forget to download the generated google-services.json and add Firebase SDKs to your android project.

Step#2: Add required dependencies

In the flutter chat_app, add the new dependencies required for the feature in the project’s pubspec.yaml.

firebase_core: ^1.7.0 # required for initializing firebase app

firebase_messaging: ^10.0.8 # required for sending/receiving messages through FCM

flutter_local_notifications: ^6.0.0 # required to show chat notifications

Download the new dependencies added above.

$ flutter pub get

Step#3: Enable your app to receive notifications

In order to create local notifications, you need to add the required intent filter in your android/app/src/main/AndroidManifest.xml.

<intent-filter>

<action android:name="FLUTTER_NOTIFICATION_CLICK" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

Step#4: Initialize Firebase Messaging and Local Notification

Create a new dart file firebase/messaging.dart in lib and initialize FCM and Local Notification in it.

import 'package:firebase_core/firebase_core.dart';

import 'package:firebase_messaging/firebase_messaging.dart';

import 'package:flutter_local_notifications/flutter_local_notifications.dart';



final FirebaseMessaging _fcm = FirebaseMessaging.instance;

FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin;



initializeMessaging() async {

await Firebase.initializeApp();

_flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings("@mipmap/ic_launcher");

final InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid);

await _flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: selectionNotification);



FirebaseMessaging.onMessage.listen((RemoteMessage message) async {

print("onMessage: $message");

await handleMessage(message);

});

}



Future<dynamic> selectionNotification(String payload) async {

print('payload: $payload');

}



handleMessage(RemoteMessage message) async {

const AndroidNotificationDetails androidPlatformChannelSpecifics =

AndroidNotificationDetails('CHAT', "CHAT", 'CHAT', importance: Importance.max, priority: Priority.high, showWhen: true);

const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics);

await _flutterLocalNotificationsPlugin.show(0, "New Messages", message.data['sender'] + ": " + message.data['message'], platformChannelSpecifics,

payload: 'CHAT');

}



Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {

await initializeMessaging();

const AndroidNotificationDetails androidPlatformChannelSpecifics =

AndroidNotificationDetails('CHAT', "CHAT", 'CHAT', importance: Importance.max, priority: Priority.high, showWhen: true);

const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics);

await _flutterLocalNotificationsPlugin.show(0, "New Messages", message.data['sender'] + ": " + message.data['message'], platformChannelSpecifics,

payload: 'CHAT');

} Future<String> getFCMToken() async {

return await _fcm.getToken();

}

Firebase.initializeApp() initializes the Firebase app using the google-services.json you downloaded in your android project.

FirebaseMessaging.onMessage.listen() listens to FCM messages and calls handleMessage() which displays the notification in your device. This handles notification when the app is in the foreground.

firebaseMessagingBackgroundHandler() handles FCM messages when the app is in the background.

getFCMToken() returns the FCM registration token. This token will be used by the websocket service to send FCM messages to offline (not connected to websocket) devices.

Note: Do not create any class in firebase/messaging.dart as firebaseMessagingBackgroundHandler() is expected to an anonymous function.

And finally, initialize FCM from main.dart.

void main() async {

WidgetsFlutterBinding.ensureInitialized();

initializeMessaging(); FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);

...

runApp(MyApp());

}

Step#5: Send FCM token to websocket service on connect

In this step, we will let the websocket service know about the FCM token of a user. In chat.dart, while connecting to websocket, add queries to send userName & FCM token.