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 onResume
callbacks. Be careful that these callbacks are only invoked when the app in the foreground, otherwise nothing happend. Details in the picture below
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
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.
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
.
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
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
Congratulations
Now we are done 👏
Source code for flutter app
and for the trigger