Flutter chat app extended — push notification messages

Duy Tran
7 min readJul 11, 2019

--

This part as the extension of the previous article to solve the requirement: how to push and receive a new message through notification.

Today we’ll implement it with firebase_messaging, Cloud Functions, local_notifications. This section is not only limited to push messages, but you can also flexibly apply to other push cases.

If you haven’t seen the previous article, maybe come here first for some steps mentioned previously.

TLDR: check source code at the end of this article.

We need the following 3 things to meet the requirement

  • firebase_messaging to register push service and listening notification coming.
  • Cloud Functions fire a callback when it is triggered by an event — new document is added.
  • local_notifications to render our local notification when app in the foreground.

Demo

1. firebase_messaging

A Flutter plugin to use the Firebase Cloud Messaging (FCM) API. With this plugin, Flutter app can receive and process push notifications as well as data messages on Android and iOS.

Notes

Push notifications have two parts: one is notification (title and message), and one is additional data. I’m going to use only the first part in this demo scope.

Notifications are sent to the app via the onMessage, onLaunch, and onResumecallbacks. Be careful that these callbacks are only invoked when the app in the foreground, otherwise nothing happend. Details in the picture below

Notification callback cases

In case you would like to navigate or do something when the user clicks on a notification instead of open the app as the default, consider using additional data since it has data payload and handle click_action: FLUTTER_NOTIFICATION_CLICK.

Notification flow

Since in this demo scope, I’m not going to handle onClick notification event (default action is opening the app) so there will be 2 situations:

  • When app in the foreground -> callbacks be invoked -> got information from them -> render our local notification.
  • When app in the background or not in the stack (be killed) -> callbacks can’t be invoked (actually we don’t care) -> OS auto show notification as to the default action.

Installing

Adding firebase_messaging: ^14.4.0 in pubspec.yaml file is done (other steps we have completed in the previous article).

Flutter integration

Structure of variable message will be different in android and ios

Android
iOS

So why I need to check platform to get the right content

requestNotificationPermissions() to bring up a permissions dialog for the user to confirm on iOS. It’s a no-op on Android.

Registering onMessage, onResume, and onLaunch callbacks via configure() to listen for incoming messages.

Finally, I getToken() to update the pushToken field of this current user to cloud firestore (for pushing programmatically by Cloud Functions later).

Now an user’s profile should have 2 more fields look like this

chattingWith existing here to detect peer user who the current user talking. To decide whether to send the notification message.

Logic case in this section is simple

  • If the current user goes to ChatScreen to talking with another user (e.g. Peter), setting chatttingWith = Peter's id=> meaning the current user receive notifications from all users except Peter.
  • If the current user go back to MainScreen (list all users), setting chatttingWith = null => meaning the current user don’t talk with any person at the moment => receiving all notifications.

Setup enviroment

Android: do nothing, everything is done.

iOS

You must have Paid Apple Developer account and at least a physical iOS device to test push notification.

In order for the article to be brief, I would skip some general steps (you can refer a lot on google) like add udid iOS device, register bundle identifier.

Then perform the following steps:

  • Generating Apple Push Notifications service (APNs) key file. This file will have the format with KeyName.p8 and download it to your computer.
  • Uploading the key you just downloaded to Project Overview/Project Settings/Cloud Messaging (file .p8, Key ID, Team ID).
  • Make sure Bundle ID the same on both Firebase and app config.
  • Turning Push Notifications ON in Xcode

Finally, rebuild the project and running it on a physical iOS device.

Quick push for testing

Now you can use Cloud Messaging to push a test notification to your device. If your previous config perfect, the notification should be shown on your device.

2. Cloud Functions

Note: not available on Spark Plan now, so my source code demo won’t work anymore, but you could still refer to this section to config your own

Cloud Functions for Firebase lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests.

Cloud Messaging console just help you push a test notification or when you need to push a message for all existing users. That’s why we need Cloud Functions here to listen when a message document is added and pushing a notification to appropriate user programmatically.

Setup

If your project doesn’t have any functions, the dashboard won’t show anything but a button to show you how to get started.

And when we have one or more functions, the dashboard will show all of them, now we can go to logs for watching data what we wrote in our function.

Now let’s start, first run command npm install -g firebase-tools to install the Firebase CLI globally via npm.

Then run firebase login to log in via the browser and authenticate the firebase tool.

Initiating the project with firebase init

Choosing Functions

Then CLI will show a list of your existing project, choose an appropriate project

With options for language support, in my case, I’m going to use JavaScript

I don’t need to use ESLint for checking style code, so just say No

Now the setup is complete, let’s move to the next.

Logic code

The content of index.js file is as follows

Explaining

document(‘messages/{groupId1}/{groupId2}/{message}’) is the path to message document.

onCreate is triggered when a document is written to for the first time ~ when a new message is created

snap param contain all information about the document just added. Then using snap.data() to read the main data we need.

snap.data() result is caught by log

First, we’re going to find the user who receives the notification (the goal is to get push token of this user) by the idFrom we got at snap.

Example an user’s info

Also checking if the user has pushToken (some devices can’t get it like China’s phone…) and chattingWith to detect whether user-receive chatting with user-send (meaning don’t push notification when they’re talking together).

Second, find the user who sent this message (basically to get the nickname of this user) by the idTo.

Then preparing payload which the data push to the target device.

Finally, push this notification by admin.messaging().sendToDevice().

Logic code is done, let push it to the cloud.

Deploying

Just running the command firebase deploy

Success

Watching logs

3. local_notifications

A Flutter plugin for displaying local notifications on Android and iOS.

Why we need this plugin?

The reason is you notice that firebase_messaging only popup our notifications when the app is in the background or terminated. It won’t popup anything when the app in the foreground, so we need to render our own notification at that time by this plugin.

Installing

Add flutter_local_notifications: ^16.1.0 in pubspec.yaml file.

Flutter Integration

Initializing the plugin, notice app_icon needs to be added at src/main/res/drawable whether the plugin will throw the confusing error which I spent half an hour to solve.

Rendering our own notification when onMessage callback at FirebaseMessaging listener is invoked

--

--