DEV Community

Cover image for Automating Push Notifications in Flutter with Firebase Cloud Functions and OneSignal.
Olamide Gabriel
Olamide Gabriel

Posted on

Automating Push Notifications in Flutter with Firebase Cloud Functions and OneSignal.

Cover Photo by Jamie Street on Unsplash

Check my previous article to learn how to set up OneSignal in Flutter.

Your database is going to get updated from time to time, and most times when they do, you want to inform your users about this change through push notifications.

What do you do when you need to send notifications to hundreds of users based on an update to your FireStore collection?

Lucky for us, OneSignal has an easy-to-use API that can be integrated with Firebase Cloud functions.

Enough of the talks, let's automate our push notifications!

Requirements

  1. You must have set up firebase authentication in your Flutter app.

If you have not set up authentication in your app, check out the official FlutterFire documentation on Firebase Authentication with Flutter to get started.

Step 1
OneSignal provides unique player IDs to identify different devices. You need to save the player IDs of every user that registers on your app.

  1. Create a Firestore collection named 'onesignalids' where you would save the player IDs for each user.

  2. Add two fields to the collection.
    (i) The customer_id field where you save the user ID of a particular user.
    (ii) An array of strings where you save the player ID of that user.

  3. Use the savePlayerId method to save the user's player id. This method should be invoked when the user registers or logs in depending on your use case.

What this method does is to create a new list of player-ids for a user, if it doesn't already exist.
FieldValue.arrayUnion() ensures that there are no duplicate player-ids in the array.

savePlayerId() async {
//Call the required instances.
    final firebaseAuth = FirebaseAuth.instance;
    final firestore = FirebaseFirestore.instance;
    try {
      final subState = await OneSignal.shared.getPermissionSubscriptionState();
      final deviceId = subState.subscriptionStatus.userId;
      final firestoreDeviceIdReference = await firestore
          .collection("onesignalids")
          .where("customer_id", isEqualTo: firebaseAuth.currentUser?.uid ?? "")
          .get();
      if (firestoreDeviceIdReference != null &&
          firestoreDeviceIdReference.size != 0) {
        await firestore
            .collection("onesignalids")
            .doc(firestoreDeviceIdReference.docs[0].id)
            .update({
          "player_ids": FieldValue.arrayUnion([deviceId])
        });
      } else {
        await firestore.collection("onesignalids").add({
          "customer_id": firebaseAuth.currentUser?.uid ?? "",
          "player_ids": [deviceId],
        });
      }
    } catch (e) {
      print("ERROR $e");
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Next, you create your notifications collection on Firebase FireStore.

  1. Create a FireStore collection named 'notifications' where you would save the notifications for all users.

  2. Add the following fields to the collection.
    (i) The customer_id field which takes the user ID of the recipient of the notification.
    (ii) The title field that takes the title of your notification.
    (iii) The detail field that takes in the notification payload.
    (iv) The time field that takes the timestamp of the notification.

PS: You can name these fields however you want to, as long as you remain consistent.

IMPORTANT: To be able to use cloud functions, you need to upgrade your project's plan to the Blaze plan. You can easily upgrade in the settings section of your project.

To be able to use cloud functions, you must have Node installed. This gives you access to run npm functions.

If you don't already have firebase tools installed,
run npm install -g firebase-tools in the command prompt.

Ensure to run firebase login in the command prompt to log in on your device.

Let's create and deploy our cloud function!

  1. Create a new folder and run firebase init functions in cmd.
  2. Select your Firebase project and be sure to install dependencies with npm.

Also, run npm install https as you will need it to make API calls.

We will make use of Javascript to write our cloud functions.

CMD IMAGE
CMD IMAGE

  1. Open the index.js in the folder in your code editor (VSCode or any preferred editor) and follow the next steps.

First, you need to load the modules you will require for this project.

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp(functions.config.functions);
 const https = require("https");

Enter fullscreen mode Exit fullscreen mode

Next, we create a method that handles sending of push notifications via an API call.

This function takes in our push notification data containing our message and the player IDs of recipients and makes a request to the OneSignal server to have them sent.

const sendNotification = (data) => {
  var headers = {
    "Content-Type": "application/json; charset=utf-8",
    Authorization: "Basic $YOUR ONESIGNAL API KEY",
  };

  var options = {
    host: "onesignal.com",
    port: 443,
    path: "/api/v1/notifications",
    method: "POST",
    headers: headers,
  };

  var req = https.request(options, (res) => {
    console.log("statusCode:", res.statusCode);
    console.log("headers:", res.headers);
    res.on("data", (data) => {
      console.log("Response:");
      console.log(JSON.parse(data));
    });
  });


  req.on("error", (e) => {
    console.log("ERROR:");
    console.log(e);
  });

  req.write(JSON.stringify(data));
  req.end();
};
Enter fullscreen mode Exit fullscreen mode

Next, we assign a function that is executed when a document is created on our notifications collection in FireStore to an export.

The 'pushNotificationMessage' export function is responsible for fetching the player IDs and notification message, and sending it to the OneSignal server for delivery to the recipients.

exports.pushNotificationMessage = functions.firestore.document("notifications/{id}").onCreate(async (snapshot, context) => {
 //OnCreate function will go here. 
});
Enter fullscreen mode Exit fullscreen mode

Let's walk through the 'onCreate' callback.

First, we fetch the player ID of the notification's recipient.


  var playerIdsDocs = await admin.firestore().collection("onesignalids")
  .where("customer_id","==" , snapshot.data().customer_id,)
  .get(); //this fetches the document containing the user's player //ids
  var playerIds = [];
  if(!playerIdsDocs.empty){
     playerIdsDocs.forEach((playerIdSingleDoc)=>{
       playerIds = playerIdSingleDoc.data().player_ids; // this //assigns the player id(s) to the list variable above.
     });
}
Enter fullscreen mode Exit fullscreen mode

Note : Only one document would be fetched for each user.

Next, we save the notification in a message variable.

var message = {
        app_id: "YOUR APP ID",
        contents: { en : snapshot.data().detail },
        headings: { en : snapshot.data().title },
        include_player_ids : playerIds,
      };
Enter fullscreen mode Exit fullscreen mode

Finally, for the callback, we call the 'sendNotification' function we defined earlier.

   if(!playerIds.empty){
      return sendNotification(message);
   }
      else{
        return ;
      }
Enter fullscreen mode Exit fullscreen mode

The only thing left to do is to open the terminal and run firebase deploy.

Every time the notifications collection is updated, OneSignal will send a notification on your behalf.

The complete cloud functions code can be viewed below.

Oldest comments (2)

Collapse
 
onesignaldevs profile image
onesignaldevs

This is great! Keep up the good work :)

Collapse
 
lordlamee profile image
Olamide Gabriel

Thank you!