Integrating Firebase Cloud Messaging API (V1) with Dart & Flutter (2024)

Enoch Aikpokpodion
4 min readJun 24, 2024

--

On June 20, 2024, Firebase finally disabled the legacy Cloud Messaging API and users of Firebase Cloud Messaging (FCM) have to fully migrate to V1. On the official documentation, it shows how to integrate this API in Node.js, Java, Python, Go and C# but not Dart. I will be showing how you can create this service in server-side Dart which can be used in Serverpod, Dartfrog and this should also work in Flutter.

DISCLAIMER: It is recommended that notification service is integrated on the server-side because it contains confidential credentials that can allow access to your cloud resources, so it should not be exposed. For the sake of developers that might be trying out FCM in personal Flutter projects without a hosted server, it is recommended that this is done with extreme care due to the risk of exposing your credentials. Check out flutter_dotenv, and Andrea Bizzotto’s guide on how to secure sensitive data in Flutter.

Step 1: Create a new project on Firebase console. If you have not added Firebase to your project, checkout this documentation that shows how to add Firebase to a flutter app.

Step 2: For users that have an existing Flutter project, go to your project settings and choose Cloud messaging. From here, click on Manage Service Accounts and it would redirect you to Google cloud console.

Step 3: Click on ‘Manage keys’ in the actions menu and create a new key. When you do, you would be promoted to choose a key type. Choose the .json format as it is recommended. Once created, this key would be downloaded to your local machine.

The data in this file should look like this:

{
"type": "service_account",
"project_id": "fcm-notification-guide",
"private_key_id": "xxxxxxxxxxxxxxxxx",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiGsg0BAgEFAASCBKcwggSjAgEAAoIBAQgLL6hflcni5Df2\nycj9S1yv1F7WscmbwcVWrL0f1Ul9cvq6Zbxd1/lu/E5fbDwhWcXvEYROvriLT+8V\ncrh07msihVexLFyOptw3YSZmp+LaeYkpVJd+mkrQCvtYluaC0C3nJmnjXUdlPVhZ\nB04tnz3XpGoiY6qfO4wFUjX9b0WPrC3eO7+ETOrxojo/cMs6n6kaYw2UFtFq99tO\nCdFYDC9a6i6p+b1/Q6vAPNZUGcAN1K3QzCdRrkSS05lJoR7xkmoXL/lcmBC1ur9/\nBKjra4QQbknFIt50PDrOMW7pyV4VvSs20CSCKRLvAF7xTfVZlBUufkh4OWakQ56N\nRQfY4b2HAgMBAAECggEABJkIe1k9X0gAyDHavXiWGVh56OvO/xpMZ+CyyuSdrBwM\n9mjU8VqG328QKvKdDbglVXqU/t8r9+MQKvEUoXi/reFMkbLKSWQRbqNhJhOIe58i\nbm/+mZvOM5T8CQ4pGy6wyzlATifPNMcYW7NhnoCq+pU63ZpZww6q5XftxtVcwpp9\ne+tk/NgtkUw47C20teY7SxOhbtCJOe07IXKzChvuJzDZAE6KA21JR5R7yHObOIng\nG1PYmTk7dnG0wKu4wS8BDztG8C+ZrlcYjZq/ThZN6l1h/z9Ba1GeA53+SHbIsash\nqgIeVqXJMCbrSQHXqcftF67wBo4hVaAWFyNccGYWSQKBgQDyZTRQaRbk+I9SiKvk\nYydp9gQVaSLRdT89yxKPghWBOxCEvXje0Jjb+qmjbxDEAH+KYXo2wGcaeoSesx+e\nKvZ3ZgEBASCsYQe5Z7Ns6U9q1c/RkpOOm4tqs6Rr2xzp1BGJJrq0T6Ta6D2rqHQr\n0Qyestzv3bmJWV3tocOwQWcHbQKBgQDWlxYffF6ExM4pwmYmm5RZ2G4SgJRcD4ki\nUGqoaC44M7OnTs4mU6Sl2PMdU/bqa2VA6xlVBae3At/GkDWl5/eRoySVXUWzDqch\nlwGruVSLvDUbY+LrR9qHu/O9aUoIHcuCyz6ipMV/rPph0bgCkF46Tg11WLQEAJjA\nEnCrs1p8QwKBgFRPRTnuw4rSta8kYH1oy428h0EXN4gZOuz4VDVcLrSrYZx59q+x\naGmjLxvshq1V6warzwF6PKJmGPjGHuo50U5ISXMa/E1Y1oHzguIP6JKlWcpwLCi/\nC2gKodbt+ImQPKUytl//D70gaErrpbST2srcNaKswVr3nYcI6/LDXN2lAoGBALm6\nFom1yNC8zXrTKDpuGV4RtmMJKl7ri1NJqqaLVMWL6gf6au49IpnwRaqyi1ilDwO6\n9zfhJyF8uVVQZz5qoJlKTFWCXtr06a5m1ilcxFIU2RoVv8bHb9pe0Dy1rj+MGiHf\n6xw15EH24tZa/xoo/YUVHQzdcyCrq4lB7IaPOtelAoGABVcbZyVFfo9isNjHbvDt\nJfQhrouSwCnTNeRvYpBsaVfIBWQWdrKwaXseJJGJ/vK5Ic+FXZFStjknlRbbeTHK\nQ+KjYQ7CjD2yVJFfKoymxgdlPytUI7kgTHHhOUcqm+misBOizqxEIobqwu+Ex2rR\n0T6MZ/e4qgJfixRwiGzrVPI=\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-jlqdn@project-name.iam.gserviceaccount.com",
"client_id": "12345678901234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-jlqdn%40project-name.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}

Step 4: It’s time to dive into the implementation on Dart 🎯. First, remember the value for “project_id” because it would be used later in the code. Also, add the following packages to your pubspec.yaml file:

  • googleapis_auth for generating access tokens for the FCM notification
  • http for making network request to fcm.googleapis.com

Step 5: Create a service class called fcm_service.dart .In this file, we would create two functions to get the access key and to send the notification to a specific device or as a topic.

import 'dart:convert';
import 'dart:io';

import 'package:googleapis_auth/auth_io.dart';
import 'package:http/http.dart' as http;

class FCMService {

//get the access token with the .json file downloaded from google cloud console
Future<String> _getAccessToken() async {
try {
//the scope url for the firebase messaging
String firebaseMessagingScope =
'https://www.googleapis.com/auth/firebase.messaging';

//get the service account from the environment variables or from the .env file where it has been stored.
//it is advised not to hardcode the service account details in the code
Map<String, dynamic> serviceAccount =
json.decode(const String.fromEnvironment('FIREBASE_SERVICE_ACCOUNT'));
final client = await clientViaServiceAccount(
ServiceAccountCredentials.fromJson(serviceAccount),
[firebaseMessagingScope]);

final accessToken = client.credentials.accessToken.data;
return accessToken;
} catch (_) {
//handle your error here
throw Exception('Error getting access token');
}
}

// SEND NOTIFICATION TO A DEVICE
Future<bool> sendNotification(
{required String recipientFCMToken,
required String title,
required String body}) async {
final String accessToken = await _getAccessToken();
//Input the project_id value in the .json file downloaded from the google cloud console
const String projectId = 'INPUT project_id VALUE HERE';
const String fcmEndpoint = "https://fcm.googleapis.com/v1/projects/$projectId";
final url = Uri.parse('$fcmEndpoint/messages:send');
final headers = {
HttpHeaders.contentTypeHeader: 'application/json',
'Authorization': 'Bearer $accessToken',
};
final reqBody = jsonEncode(
{
"message": {
"token": recipientFCMToken,
"notification": {"body": body, "title": title},
"android": {
"notification": {
"click_action": "FLUTTER_NOTIFICATION_CLICK",
}
},
"apns": {
"payload": {
"aps": {"category": "NEW_NOTIFICATION"}
}
}
}
},
);

try {
final response = await http.post(url, headers: headers, body: reqBody);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
} catch (_) {
//handle your error here
return false;
}
}
}

The code for the fcm_service.dart file can be found here

Basic example of usage

This is an example of how you can use this sendNotification method in the FCMService. Take a look at this method that send a notification to a recepient when a message is sent:

 Future<bool> notifyRecipient(User user,Message message) async {
String fcmToken = user.fcmToken;
return await FCMService().sendNotification(
recipientFCMToken: fcmToken,
title: 'New message from ${message.sender}',
body: message.body,
);
}
  1. Pass the device token of the recipient in recipientFCMToken
  2. Pass the title and body of the notification

What did we just do?

  • We just created a service account key for our project
  • We then created a Firebase Cloud Messaging service that allows us to generate access key and send notifications easily.

For customization of the payload used in sending the notification, kindly visit the firebase docs and blog.

Thank you for staying till the end, this is my first-ever article on Flutter and I hope at-least helped someone, if so, don’t forget to CLAP👏

--

--

Enoch Aikpokpodion
Enoch Aikpokpodion

Written by Enoch Aikpokpodion

A Software Engineer whose aim is to solve problems and share informative contents to his readers #Software Engineer #Flutter.

Responses (2)