Bergantung pada status perangkat, pesan masuk akan ditangani secara berbeda. Untuk memahami skenario ini dan cara mengintegrasikan FCM ke dalam aplikasi Anda sendiri, sangat penting untuk menetapkan berbagai status perangkat terlebih dahulu:
Status | Deskripsi |
---|---|
Latar depan | Saat aplikasi dibuka, dilihat, dan digunakan. |
Latar belakang | Saat aplikasi dibuka, tetapi di latar belakang (diminimalkan). Hal ini biasanya terjadi saat pengguna menekan tombol "layar utama" di perangkat, beralih ke aplikasi lain menggunakan pengalih aplikasi, atau membuka aplikasi di tab yang berbeda (web). |
Dihentikan | Saat perangkat terkunci atau aplikasi tidak berjalan. |
Ada beberapa prasyarat yang harus dipenuhi sebelum aplikasi dapat menerima payload pesan melalui FCM:
- Aplikasi harus sudah dibuka setidaknya satu kali (untuk memungkinkan pendaftaran ke FCM).
- Di iOS, jika pengguna menutup aplikasi dari pengalih aplikasi, aplikasi harus dibuka kembali secara manual agar pesan di latar belakang mulai berfungsi lagi.
- Di Android, jika pengguna memaksa keluar dari aplikasi dari setelan perangkat, aplikasi harus dibuka kembali secara manual agar pesan mulai berfungsi.
- Di web, Anda harus sudah meminta token (menggunakan
getToken()
) dengan sertifikat web push Anda.
Meminta izin untuk menerima pesan
Di iOS, macOS, web, dan Android 13 (atau yang lebih baru), sebelum payload FCM dapat diterima di perangkat Anda, Anda harus terlebih dahulu meminta izin pengguna.
Paket firebase_messaging
menyediakan API sederhana untuk meminta izin melalui metode requestPermission
.
API ini menerima sejumlah argumen bernama yang menentukan jenis izin yang ingin Anda minta, seperti apakah pesan yang berisi payload notifikasi dapat memicu suara atau membaca pesan melalui Siri. Secara default, metode ini meminta izin default yang logis. API referensi memberikan dokumentasi lengkap tentang fungsi dari setiap izin.
Untuk memulai, panggil metode tersebut dari aplikasi Anda (di iOS, modal native akan ditampilkan, sedangkan di web, alur API native browser akan dipicu):
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
Properti authorizationStatus
dari objek NotificationSettings
yang ditampilkan dari permintaan dapat digunakan untuk menentukan keputusan keseluruhan pengguna:
authorized
: Pengguna memberikan izin.denied
: Pengguna menolak izin.notDetermined
: Pengguna belum memilih apakah akan memberikan izin atau tidak.provisional
: Pengguna memberikan izin sementara
Properti lainnya di NotificationSettings
akan menampilkan apakah izin tertentu diaktifkan, dinonaktifkan, atau tidak didukung di perangkat saat ini.
Setelah izin diberikan dan berbagai jenis status perangkat dipahami, aplikasi Anda kini dapat mulai menangani payload FCM yang masuk.
Penanganan pesan
Berdasarkan status aplikasi Anda saat ini, payload yang masuk dari berbagai jenis pesan memerlukan implementasi yang berbeda untuk menanganinya:
Pesan latar depan
Untuk menangani pesan saat aplikasi berada di latar depan, proses aliran data onMessage
.
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
Aliran data berisi RemoteMessage
, yang memerinci berbagai informasi tentang payload, seperti asalnya, ID unik, waktu pengiriman, apakah payload berisi notifikasi, dan lainnya. Karena pesan diambil saat aplikasi berada di latar depan, Anda dapat langsung mengakses status dan konteks aplikasi Flutter.
Pesan Notifikasi dan Latar Depan
Pesan notifikasi yang masuk saat aplikasi berada di latar depan secara default tidak akan menampilkan notifikasi yang terlihat, baik di Android maupun iOS. Namun, Anda dapat mengganti perilaku ini:
- Di Android, Anda harus membuat saluran notifikasi "Prioritas Tinggi".
- Di iOS, Anda dapat memperbarui opsi tampilan untuk aplikasi.
Pesan latar belakang
Proses penanganan pesan latar belakang berbeda pada platform native (Android dan Apple) dan platform berbasis web.
Platform Apple dan Android
Tangani pesan latar belakang dengan mendaftarkan pengendali onBackgroundMessage
. Saat pesan diterima, isolate muncul (khusus Android, iOS/macOS tidak memerlukan isolate terpisah) yang memungkinkan Anda menangani pesan bahkan saat aplikasi tidak berjalan.
Ada beberapa hal yang perlu diingat tentang pengendali pesan latar belakang:
- Pengendali tidak boleh berupa fungsi anonim.
- Pengendali harus berupa fungsi tingkat atas (misalnya, bukan metode class yang memerlukan inisialisasi).
- Saat menggunakan Flutter versi 3.3.0 atau yang lebih baru, pengendali pesan harus dianotasi dengan
@pragma('vm:entry-point')
tepat di atas deklarasi fungsi (jika tidak, akan dihapus selama tree shaking untuk mode rilis).
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Karena pengendali berjalan di isolate miliknya sendiri di luar konteks aplikasi, Anda tidak dapat memperbarui status aplikasi atau menjalankan logika yang memengaruhi UI. Namun, Anda dapat menjalankan logika seperti permintaan HTTP, menjalankan operasi IO (misalnya, memperbarui penyimpanan lokal), berkomunikasi dengan plugin lain, dll.
Sebaiknya Anda menyelesaikan logika sesegera mungkin. Menjalankan tugas berdurasi lama dan secara intensif akan memengaruhi performa perangkat dan dapat menyebabkan OS menghentikan prosesnya. Jika tugas berjalan lebih dari 30 detik, perangkat mungkin akan otomatis menghentikan prosesnya.
Web
Di Web, tulis Service Worker JavaScript yang berjalan di latar belakang. Gunakan service worker untuk menangani pesan latar belakang.
Untuk memulai, buat file baru di direktori web
, yaitu file firebase-messaging-sw.js
:
// Please see this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/master/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
File tersebut harus mengimpor SDK aplikasi dan pesan, melakukan inisialisasi Firebase, dan mengekspos variabel messaging
.
Selanjutnya, worker harus didaftarkan. Dalam file index.html
, daftarkan worker dengan mengubah tag <script>
yang melakukan bootstrap pada Flutter:
<script src="flutter_bootstrap.js" async>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
});
}
</script>
Jika masih menggunakan sistem template lama, Anda dapat mendaftarkan worker dengan mengubah tag <script>
yang melakukan bootstrap pada Flutter sebagai berikut:
<html>
<body>
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Register Firebase Messaging service worker.
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl =
'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.'
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
Selanjutnya, mulai ulang aplikasi Flutter Anda. Worker akan terdaftar dan pesan latar belakang akan ditangani melalui file ini.
Menangani Interaksi
Karena notifikasi adalah tanda yang terlihat, biasanya pengguna berinteraksi dengannya (dengan menekan). Perilaku default pada Android dan iOS adalah membuka aplikasi. Jika dihentikan, aplikasi akan dimulai; jika di latar belakang, aplikasi akan dialihkan ke latar depan.
Bergantung pada konten notifikasi, Anda mungkin ingin menangani interaksi pengguna saat aplikasi terbuka. Misalnya, jika pesan chat baru dikirim melalui notifikasi dan pengguna menekannya, sebaiknya Anda membuka percakapan tertentu saat aplikasi terbuka.
Paket firebase-messaging
menyediakan dua cara untuk menangani interaksi ini:
getInitialMessage()
: Jika aplikasi dibuka dari status dihentikan,Future
yang berisiRemoteMessage
akan ditampilkan. Setelah digunakan,RemoteMessage
akan dihapus.onMessageOpenedApp
:Stream
yang mempostingRemoteMessage
saat aplikasi dibuka dari status latar belakang.
Sebaiknya kedua skenario ditangani untuk memastikan UX yang lancar bagi pengguna Anda. Contoh kode di bawah ini menguraikan cara melakukannya:
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
// It is assumed that all messages contain a data field with the key 'type'
Future<void> setupInteractedMessage() async {
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
_handleMessage(initialMessage);
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
}
void _handleMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
Navigator.pushNamed(context, '/chat',
arguments: ChatArguments(message),
);
}
}
@override
void initState() {
super.initState();
// Run code required to handle interacted messages in an async function
// as initState() must not be async
setupInteractedMessage();
}
@override
Widget build(BuildContext context) {
return Text("...");
}
}
Cara Anda menangani interaksi bergantung pada penyiapan aplikasi Anda. Contoh di atas menampilkan ilustrasi dasar menggunakan StatefulWidget.
Melokalkan Pesan
Anda dapat mengirim string yang dilokalkan dengan dua cara yang berbeda:
- Simpan bahasa pilihan setiap pengguna di server Anda dan kirim notifikasi yang disesuaikan untuk setiap bahasa
- Sematkan string yang dilokalkan di aplikasi Anda dan manfaatkan setelan lokal native sistem operasi
Berikut ini cara menggunakan metode kedua:
Android
Tentukan pesan bahasa default Anda di
resources/values/strings.xml
:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
Tentukan terjemahan pesan di direktori
values-language
. Misalnya, tentukan pesan bahasa Prancis diresources/values-fr/strings.xml
:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
Di payload server, gunakan
title_loc_key
danbody_loc_key
untuk pesan yang dilokalkan, bukan kuncititle
,message
, sertabody
, dan tetapkan ke atributname
pesan yang ingin Anda tampilkan.Payload pesan akan terlihat seperti ini:
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" } }
iOS
Tentukan pesan bahasa default Anda di
Base.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
Tentukan terjemahan pesan di direktori
language.lproj
. Misalnya, tentukan pesan bahasa Prancis difr.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
Payload pesan akan terlihat seperti ini:
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" } }
Mengaktifkan ekspor data pengiriman pesan
Anda dapat mengekspor data pesan ke BigQuery untuk dianalisis lebih lanjut. Dengan BigQuery, Anda dapat menganalisis data menggunakan BigQuery SQL, mengekspornya ke penyedia cloud lain, atau menggunakan data tersebut untuk model ML kustom Anda. Ekspor ke BigQuery mencakup semua data yang tersedia untuk pesan, terlepas dari jenis pesan atau apakah pesan dikirim melalui API atau Notifications Composer.
Untuk mengaktifkan ekspor, pertama-tama ikuti langkah-langkah yang dijelaskan di sini, lalu ikuti petunjuk berikut:
Android
Anda dapat menggunakan kode berikut:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
Untuk iOS, Anda perlu mengubah AppDelegate.m
dengan konten berikut.
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end
Web
Untuk Web, Anda perlu mengubah service worker agar dapat menggunakan SDK versi v9.
Versi v9 harus dipaketkan, sehingga Anda perlu menggunakan bundler seperti esbuild
, misalnya, agar service worker dapat berfungsi.
Lihat aplikasi contoh untuk mengetahui cara mencapainya.
Setelah bermigrasi ke SDK v9, Anda dapat menggunakan kode berikut:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Jangan lupa menjalankan yarn build
untuk mengekspor service worker versi baru Anda ke folder web
.
Menampilkan gambar dalam notifikasi di iOS
Di perangkat Apple, agar Notifikasi FCM masuk menampilkan gambar dari payload FCM, Anda harus menambahkan ekstensi layanan notifikasi tambahan dan mengonfigurasi aplikasi untuk menggunakannya.
Jika menggunakan autentikasi ponsel Firebase, Anda harus menambahkan pod Firebase Auth ke Podfile Anda.
Langkah 1 - Tambahkan ekstensi layanan notifikasi
- Di Xcode, klik File > New > Target...
- Modal akan menampilkan daftar kemungkinan target. Scroll ke bawah atau gunakan filter untuk memilih Notification Service Extension. Klik Next.
- Tambahkan nama produk (gunakan "ImageNotification" untuk mengikuti tutorial ini), setel bahasa ke Objective-C, lalu klik Finish.
- Aktifkan skema dengan mengklik Activate.
Langkah 2 - Tambahkan target ke Podfile
Pastikan ekstensi baru Anda memiliki akses ke pod Firebase/Messaging
dengan menambahkannya di Podfile:
Dari Navigator, buka Podfile: Pods > Podfile
Scroll ke bagian bawah file, lalu tambahkan:
target 'ImageNotification' do use_frameworks! pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication pod 'Firebase/Messaging' end
Instal atau update pod Anda menggunakan
pod install
dari direktoriios
ataumacos
.
Langkah 3 - Gunakan helper ekstensi
Pada tahap ini, semuanya akan tetap berjalan seperti biasa. Langkah terakhir adalah memanggil helper ekstensi.
Dari navigator, pilih ekstensi ImageNotification Anda
Buka file
NotificationService.m
.Di bagian atas file, impor
FirebaseMessaging.h
tepat setelahNotificationService.h
seperti yang ditunjukkan di bawah.Ganti konten
NotificationService.m
dengan:#import "NotificationService.h" #import "FirebaseMessaging.h" #import "FirebaseAuth.h" // Add this line if you are using FirebaseAuth phone authentication #import <UIKit/UIKit.h> // Add this line if you are using FirebaseAuth phone authentication @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService /* Uncomment this if you are using Firebase Auth - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { if ([[FIRAuth auth] canHandleURL:url]) { return YES; } return NO; } - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { for (UIOpenURLContext *urlContext in URLContexts) { [FIRAuth.auth canHandleURL:urlContext.URL]; } } */ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // Modify the notification content here... [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end
Langkah 4 - Tambahkan gambar ke payload
Dalam payload notifikasi, Anda kini dapat menambahkan gambar. Baca dokumentasi iOS tentang cara membuat permintaan kirim. Perlu diingat bahwa ukuran gambar maksimum 300 KB diterapkan oleh perangkat.