Je nach Gerätestatus werden eingehende Nachrichten unterschiedlich behandelt. Um diese Szenarien zu verstehen und FCM in Ihre eigene Anwendung einzubinden, ist es wichtig, die verschiedenen Status zu kennen, in denen sich ein Gerät befinden kann:
Status | Beschreibung |
---|---|
Vordergrund | Wenn die Anwendung geöffnet, sichtbar und in Verwendung ist. |
Hintergrund | Wenn die Anwendung geöffnet, aber im Hintergrund (minimiert) ist. Das passiert in der Regel, wenn der Nutzer die Startbildschirmtaste auf dem Gerät gedrückt, über den App-Schnellzugriff zu einer anderen App gewechselt oder die Anwendung in einem anderen Tab geöffnet hat (Web). |
Beendet | Wenn das Gerät gesperrt ist oder die App nicht ausgeführt wird. |
Es gibt einige Voraussetzungen, die erfüllt sein müssen, damit die Anwendung Nachrichtenn-Nutzlast über FCM empfangen kann:
- Die App muss mindestens einmal geöffnet worden sein, damit eine Registrierung bei FCM möglich ist.
- Wenn der Nutzer unter iOS die App aus dem App-Schnellzugriff wischt, muss sie manuell wieder geöffnet werden, damit Hintergrundnachrichten wieder funktionieren.
- Wenn der Nutzer die App unter Android über die Geräteeinstellungen beendet, muss sie manuell wieder geöffnet werden, damit Nachrichten funktionieren.
- Im Web müssen Sie mit Ihrem Web-Push-Zertifikat ein Token (mit
getToken()
) angefordert haben.
Berechtigung zum Empfangen von Nachrichten anfordern
Unter iOS, macOS, im Web und unter Android 13 (oder höher) müssen Sie die Berechtigung des Nutzers einholen, bevor FCM-Nutzlast auf Ihrem Gerät empfangen werden kann.
Das Paket firebase_messaging
bietet eine einfache API zum Anfordern einer Berechtigung über die Methode requestPermission
.
Diese API akzeptiert eine Reihe von benannten Argumenten, die die Art der Berechtigungen definieren, die Sie anfordern möchten. So können Sie beispielsweise festlegen, ob Nachrichten mit Benachrichtigungsnutzlasten einen Ton auslösen oder Nachrichten über Siri vorlesen können. Standardmäßig werden mit der Methode sensible Standardberechtigungen angefordert. Die Referenz-API enthält eine vollständige Dokumentation zu den einzelnen Berechtigungen.
Rufen Sie zuerst die Methode aus Ihrer Anwendung auf. Auf iOS-Geräten wird ein natives Modal angezeigt, im Web wird der native API-Vorgang des Browsers ausgelöst:
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}');
Mit dem Attribut authorizationStatus
des von der Anfrage zurückgegebenen NotificationSettings
-Objekts kann die Gesamtentscheidung des Nutzers ermittelt werden:
authorized
: Der Nutzer hat die Berechtigung erteilt.denied
: Der Nutzer hat die Berechtigung abgelehnt.notDetermined
: Der Nutzer hat noch nicht entschieden, ob er eine Berechtigung erteilen möchte.provisional
: Der Nutzer hat eine vorläufige Berechtigung erteilt
Die anderen Properties unter NotificationSettings
geben an, ob eine bestimmte Berechtigung auf dem aktuellen Gerät aktiviert, deaktiviert oder nicht unterstützt wird.
Sobald die Berechtigung erteilt wurde und die verschiedenen Gerätestatus bekannt sind, kann Ihre Anwendung mit der Verarbeitung der eingehenden FCM-Nutzlast beginnen.
Nachrichtenverarbeitung
Je nach aktuellem Status Ihrer Anwendung erfordern eingehende Nutzlasten verschiedener Nachrichtentypen unterschiedliche Implementierungen:
Nachrichten im Vordergrund
Wenn Sie Nachrichten verarbeiten möchten, während Ihre Anwendung im Vordergrund ausgeführt wird, hören Sie sich den onMessage
-Stream an.
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}');
}
});
Der Stream enthält eine RemoteMessage
mit verschiedenen Informationen zur Nutzlast, z. B. Herkunft, eindeutige ID, gesendete Uhrzeit und ob eine Benachrichtigung enthalten war. Da die Nachricht abgerufen wurde, während sich Ihre Anwendung im Vordergrund befindet, können Sie direkt auf den Status und den Kontext Ihrer Flutter-Anwendung zugreifen.
Nachrichten im Vordergrund und Benachrichtigungen
Benachrichtigungen, die eintreffen, während die App im Vordergrund ausgeführt wird, werden sowohl unter Android als auch unter iOS standardmäßig nicht angezeigt. Es ist jedoch möglich, dieses Verhalten außer Kraft zu setzen:
- Unter Android müssen Sie einen Benachrichtigungskanal mit der Priorität „Hoch“ erstellen.
- Auf iOS-Geräten können Sie die Präsentationsoptionen für die App aktualisieren.
Hintergrundnachrichten
Die Verarbeitung von Hintergrundnachrichten unterscheidet sich auf nativen (Android und Apple) und webbasierten Plattformen.
Apple-Plattformen und Android
Registrieren Sie einen onBackgroundMessage
-Handler, um Hintergrundnachrichten zu verarbeiten. Wenn Nachrichten empfangen werden, wird ein Isolate erstellt (nur Android, iOS/macOS erfordern kein separates Isolate). So können Sie Nachrichten auch dann verarbeiten, wenn Ihre Anwendung nicht ausgeführt wird.
Beachten Sie bei Ihrem Handler für Hintergrundnachrichten Folgendes:
- Es darf sich nicht um eine anonyme Funktion handeln.
- Es muss sich um eine Funktion der obersten Ebene handeln (z.B. keine Klassenmethode, die eine Initialisierung erfordert).
- Wenn Sie Flutter-Version 3.3.0 oder höher verwenden, muss der Nachrichtenhandler direkt über der Funktionsdeklaration mit
@pragma('vm:entry-point')
annotiert werden. Andernfalls wird er beim Tree Shaking für den Release-Modus möglicherweise entfernt.
@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());
}
Da der Handler in einem eigenen Isolate außerhalb des Anwendungskontexts ausgeführt wird, ist es nicht möglich, den Anwendungsstatus zu aktualisieren oder Logik auszuführen, die sich auf die Benutzeroberfläche auswirkt. Sie können jedoch Logik wie HTTP-Anfragen ausführen, E/A-Vorgänge ausführen (z.B. lokalen Speicher aktualisieren), mit anderen Plug-ins kommunizieren usw.
Wir empfehlen Ihnen außerdem, die Logik so schnell wie möglich fertigzustellen. Das Ausführen langer, intensiver Aufgaben wirkt sich auf die Geräteleistung aus und kann dazu führen, dass das Betriebssystem den Prozess beendet. Wenn Tasks länger als 30 Sekunden laufen, wird der Prozess möglicherweise automatisch vom Gerät beendet.
Web
Schreiben Sie im Web einen JavaScript-Service Worker, der im Hintergrund ausgeführt wird. Verwenden Sie den Dienst-Worker, um Hintergrundnachrichten zu verarbeiten.
Erstellen Sie zuerst eine neue Datei im Verzeichnis web
und nennen Sie sie 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);
});
Die Datei muss sowohl das App- als auch das Messaging-SDK importieren, Firebase initialisieren und die Variable messaging
freigeben.
Als Nächstes muss der Mitarbeiter registriert werden. Registrieren Sie den Worker in der Datei index.html
, indem Sie das <script>
-Tag ändern, das Flutter startet:
<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>
Wenn Sie noch das alte templating-System verwenden, können Sie den Worker registrieren, indem Sie das <script>
-Tag ändern, das Flutter startet:
<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>
Starten Sie als Nächstes Ihre Flutter-Anwendung neu. Der Worker wird registriert und alle Hintergrundnachrichten werden über diese Datei verarbeitet.
Interaktionen verarbeiten
Da Benachrichtigungen ein sichtbarer Hinweis sind, ist es üblich, dass Nutzer mit ihnen interagieren (indem sie darauf tippen). Sowohl unter Android als auch unter iOS wird standardmäßig die App geöffnet. Wenn die Anwendung beendet wurde, wird sie gestartet. Wenn sie sich im Hintergrund befindet, wird sie in den Vordergrund gebracht.
Je nach Inhalt einer Benachrichtigung können Sie die Interaktion des Nutzers beim Öffnen der Anwendung steuern. Wenn beispielsweise eine neue Chatnachricht über eine Benachrichtigung gesendet wird und der Nutzer darauf klickt, können Sie die entsprechende Unterhaltung öffnen, wenn die Anwendung geöffnet wird.
Das firebase-messaging
-Paket bietet zwei Möglichkeiten, diese Interaktion zu verarbeiten:
getInitialMessage()
: Wenn die Anwendung aus einem beendeten Status geöffnet wird, wird eineFuture
mit einerRemoteMessage
zurückgegeben. Nach dem Verbrauch wirdRemoteMessage
entfernt.onMessageOpenedApp
: EinStream
, das eineRemoteMessage
postet, wenn die Anwendung aus einem Hintergrundstatus geöffnet wird.
Es wird empfohlen, beide Szenarien zu berücksichtigen, um eine reibungslose User Experience zu gewährleisten. Im folgenden Codebeispiel wird gezeigt, wie das geht:
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("...");
}
}
Wie Sie mit Interaktionen umgehen, hängt von der Konfiguration Ihrer Anwendung ab. Das obige Beispiel zeigt eine einfache Abbildung mit einem StatefulWidget.
Nachrichten lokalisieren
Es gibt zwei Möglichkeiten, lokalisierte Strings zu senden:
- Die bevorzugte Sprache jedes Nutzers auf Ihrem Server speichern und personalisierte Benachrichtigungen für jede Sprache senden
- Lokalisierte Strings in Ihre App einbetten und die Einstellungen für die Sprache des Betriebssystems verwenden
So verwenden Sie die zweite Methode:
Android
Geben Sie in
resources/values/strings.xml
die Nachrichten in der Standardsprache an:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
Geben Sie die übersetzten Nachrichten im Verzeichnis
values-language
an. So geben Sie beispielsweise französische Nachrichten inresources/values-fr/strings.xml
an:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
Verwenden Sie in der Servernutzlast statt der Schlüssel
title
,message
undbody
die Schlüsseltitle_loc_key
undbody_loc_key
für Ihre lokalisierte Nachricht und stellen Sie sie auf das Attributname
ein, das der Nachricht angezeigt werden soll.Die Nachrichtenn-Nutzlast würde so aussehen:
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" } }
iOS
Geben Sie Ihre Nachrichten in der Standardsprache auf
Base.lproj/Localizable.strings
an:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
Geben Sie die übersetzten Nachrichten im Verzeichnis
language.lproj
an. Geben Sie beispielsweise französische Nachrichten infr.lproj/Localizable.strings
an:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
Die Nachrichtennutzlast würde so aussehen:
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" } }
Export von Daten zur Nachrichtenübermittlung aktivieren
Sie können Ihre Nachrichtendaten zur weiteren Analyse in BigQuery exportieren. In BigQuery können Sie die Daten mit BigQuery SQL analysieren, in einen anderen Cloud-Anbieter exportieren oder für Ihre benutzerdefinierten ML-Modelle verwenden. Ein Export nach BigQuery enthält alle verfügbaren Daten für Nachrichten, unabhängig vom Nachrichtentyp oder davon, ob die Nachricht über die API oder den Benachrichtigungs-Editor gesendet wird.
Wenn Sie den Export aktivieren möchten, führen Sie zuerst die hier beschriebenen Schritte aus und gehen Sie dann so vor:
Android
Sie können den folgenden Code verwenden:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
Für iOS musst du AppDelegate.m
mit dem folgenden Inhalt ändern.
#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
Für das Web müssen Sie Ihren Service Worker ändern, um die Version 9 des SDKs zu verwenden.
Die Version 9 muss gebundelt werden. Sie müssen also einen Bundler wie esbuild
verwenden, damit der Service Worker funktioniert.
In der Beispiel-App sehen Sie, wie das geht.
Nachdem Sie zur Version 9 des SDK migriert sind, können Sie den folgenden Code verwenden:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Vergessen Sie nicht, yarn build
auszuführen, um die neue Version Ihres Service Workers in den Ordner web
zu exportieren.
Bilder in Benachrichtigungen auf iOS-Geräten anzeigen
Damit auf Apple-Geräten eingehende FCM-Benachrichtigungen Bilder aus der FCM-Nutzlast anzeigen, müssen Sie eine zusätzliche Benachrichtigungsdiensterweiterung hinzufügen und Ihre App so konfigurieren, dass sie verwendet wird.
Wenn Sie die Firebase-Telefonauthentifizierung verwenden, müssen Sie Ihrer Podfile den Firebase Auth-Pod hinzufügen.
Schritt 1: Erweiterung für Benachrichtigungsdienst hinzufügen
- Klicken Sie in Xcode auf File > New > Target… (Datei > Neu > Ziel…).
- Ein modales Fenster zeigt eine Liste der möglichen Ziele an. Scrollen Sie nach unten oder verwenden Sie den Filter, um Notification Service Extension auszuwählen. Klicken Sie auf Next.
- Fügen Sie einen Produktnamen hinzu (verwenden Sie „ImageNotification“, um dieser Anleitung zu folgen), legen Sie die Sprache auf „Objective-C“ fest und klicken Sie auf Fertigstellen.
- Klicken Sie auf Aktivieren, um das Schema zu aktivieren.
Schritt 2: Ziel zur Podfile hinzufügen
Achten Sie darauf, dass Ihre neue Erweiterung Zugriff auf den Firebase/Messaging
-Pod hat, indem Sie sie in die Podfile einfügen:
Öffnen Sie die Podfile-Datei im Navigationsbereich: Pods > Podfile.
Scrollen Sie zum Ende der Datei und fügen Sie Folgendes hinzu:
target 'ImageNotification' do use_frameworks! pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication pod 'Firebase/Messaging' end
Installieren oder aktualisieren Sie Ihre Pods mit
pod install
aus dem Verzeichnisios
odermacos
.
Schritt 3: Erweiterungsassistent verwenden
Bis dahin sollte alles wie gewohnt funktionieren. Im letzten Schritt rufen Sie den Erweiterungsassistenten auf.
Wähle im Navigator die ImageNotification-Erweiterung aus.
Öffnen Sie die Datei
NotificationService.m
.Importieren Sie oben in der Datei
FirebaseMessaging.h
direkt nachNotificationService.h
, wie unten dargestellt.Ersetzen Sie den Inhalt von
NotificationService.m
durch Folgendes:#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
Schritt 4: Bild zur Nutzlast hinzufügen
Sie können Ihrer Benachrichtigungsnutzlast jetzt ein Bild hinzufügen. Weitere Informationen zum Erstellen einer Sendeanfrage finden Sie in der iOS-Dokumentation unter Sendeanfrage erstellen. Beachten Sie, dass das Gerät eine maximale Bildgröße von 300 KB durchsetzt.