Empfangen Sie Nachrichten in einer Flutter-App

Abhängig vom Zustand eines Geräts werden eingehende Nachrichten unterschiedlich behandelt. Um diese Szenarien zu verstehen und wie Sie FCM in Ihre eigene Anwendung integrieren können, ist es zunächst wichtig, die verschiedenen Zustände zu ermitteln, in denen sich ein Gerät befinden kann:

Zustand Beschreibung
Vordergrund Wenn die Anwendung geöffnet, angezeigt und verwendet wird.
Hintergrund Wenn die Anwendung geöffnet ist, jedoch im Hintergrund (minimiert). Dies geschieht typischerweise, wenn der Benutzer die „Home“-Taste auf dem Gerät gedrückt hat, mit dem App-Umschalter zu einer anderen App gewechselt ist oder die Anwendung in einem anderen Tab (Web) geöffnet hat.
Beendet Wenn das Gerät gesperrt ist oder die Anwendung nicht ausgeführt wird.

Damit die Anwendung Nachrichtennutzdaten über FCM empfangen kann, müssen einige Voraussetzungen erfüllt sein:

  • Der Antrag muss mindestens einmal geöffnet worden sein (um eine Registrierung bei FCM zu ermöglichen).
  • Wenn der Benutzer unter iOS die Anwendung vom App-Umschalter wegwischt, muss sie manuell erneut geöffnet werden, damit Hintergrundnachrichten wieder funktionieren.
  • Wenn der Benutzer unter Android das Beenden der App über die Geräteeinstellungen erzwingt, muss sie manuell erneut geöffnet werden, damit die Nachrichten funktionieren.
  • Im Web müssen Sie mit Ihrem Web-Push-Zertifikat ein Token (mit getToken() ) angefordert haben.

Fordern Sie die Erlaubnis zum Empfangen von Nachrichten an

Unter iOS, macOS, Web und Android 13 (oder neuer) müssen Sie zunächst die Erlaubnis des Benutzers einholen, bevor FCM-Payloads auf Ihrem Gerät empfangen werden können.

Das Paket firebase_messaging stellt eine einfache API zum Anfordern von Berechtigungen über die Methode requestPermission bereit. Diese API akzeptiert eine Reihe benannter Argumente, die die Art der Berechtigungen definieren, die Sie anfordern möchten, z. B. ob Nachrichten mit Benachrichtigungsnutzlasten einen Ton auslösen oder Nachrichten über Siri vorlesen können. Standardmäßig fordert die Methode sinnvolle Standardberechtigungen an. Die Referenz-API bietet eine vollständige Dokumentation darüber, wozu jede Berechtigung dient.

Rufen Sie zunächst die Methode aus Ihrer Anwendung auf (unter iOS wird ein natives Modal angezeigt, im Web wird der native API-Flow 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}');

Die Eigenschaft authorizationStatus des von der Anfrage zurückgegebenen Objekts NotificationSettings kann verwendet werden, um die Gesamtentscheidung des Benutzers zu bestimmen:

  • authorized : Der Benutzer hat die Berechtigung erteilt.
  • denied : Der Benutzer hat die Berechtigung verweigert.
  • notDetermined : Der Benutzer hat noch nicht entschieden, ob er die Berechtigung erteilen möchte.
  • provisional : Der Benutzer hat eine vorläufige Berechtigung erteilt

Die anderen Eigenschaften von NotificationSettings geben zurück, 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ätestatustypen verstanden wurden, kann Ihre Anwendung nun mit der Verarbeitung der eingehenden FCM-Nutzlasten beginnen.

Nachrichtenverarbeitung

Basierend auf dem aktuellen Status Ihrer Anwendung erfordern eingehende Nutzlasten unterschiedlicher Nachrichtentypen unterschiedliche Implementierungen, um sie zu verarbeiten:

Vordergrundnachrichten

Um Nachrichten zu verarbeiten, während sich Ihre Anwendung im Vordergrund befindet, hören Sie den onMessage Stream ab.

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 über die Nutzlast, z. B. woher sie stammt, die eindeutige ID, die Sendezeit, ob sie eine Benachrichtigung enthielt und mehr. Da die Nachricht abgerufen wurde, während Ihre Anwendung im Vordergrund läuft, können Sie direkt auf den Status und Kontext Ihrer Flutter-Anwendung zugreifen.

Vordergrund- und Benachrichtigungsnachrichten

Benachrichtigungsnachrichten, die eingehen, während sich die Anwendung im Vordergrund befindet, zeigen sowohl auf Android als auch auf iOS standardmäßig keine sichtbare Benachrichtigung an. Es ist jedoch möglich, dieses Verhalten zu überschreiben:

  • Unter Android müssen Sie einen Benachrichtigungskanal mit „hoher Priorität“ erstellen.
  • Unter iOS können Sie die Präsentationsoptionen für die Anwendung aktualisieren.

Hintergrundmeldungen

Der Umgang mit Hintergrundnachrichten unterscheidet sich auf nativen (Android und Apple) und webbasierten Plattformen.

Apple-Plattformen und Android

Behandeln Sie Hintergrundnachrichten, indem Sie einen onBackgroundMessage Handler registrieren. Wenn Nachrichten empfangen werden, wird ein Isolat erzeugt (nur Android, iOS/macOS erfordert kein separates Isolat), sodass Sie Nachrichten auch dann verarbeiten können, wenn Ihre Anwendung nicht ausgeführt wird.

Bei Ihrem Hintergrundnachrichten-Handler sind einige Dinge zu beachten:

  1. Es darf keine anonyme Funktion sein.
  2. Es muss eine Funktion der obersten Ebene sein (z. B. keine Klassenmethode, die eine Initialisierung erfordert).
  3. Bei Verwendung von Flutter Version 3.3.0 oder höher muss der Nachrichtenhandler mit @pragma('vm:entry-point') direkt über der Funktionsdeklaration annotiert werden (andernfalls kann er beim Tree Shaking für den Release-Modus entfernt werden).
@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 Isolat außerhalb Ihres Anwendungskontexts ausgeführt wird, ist es nicht möglich, den Anwendungsstatus zu aktualisieren oder eine Logik auszuführen, die sich auf die Benutzeroberfläche auswirkt. Sie können jedoch Logik wie HTTP-Anfragen ausführen, IO-Vorgänge ausführen (z. B. den lokalen Speicher aktualisieren), mit anderen Plugins kommunizieren usw.

Es wird außerdem empfohlen, Ihre Logik so schnell wie möglich zu vervollständigen. Das Ausführen langer, intensiver Aufgaben beeinträchtigt die Geräteleistung und kann dazu führen, dass das Betriebssystem den Prozess abbricht. Wenn Aufgaben länger als 30 Sekunden ausgeführt werden, bricht das Gerät den Prozess möglicherweise automatisch ab.

Netz

Schreiben Sie im Web einen JavaScript- Service-Worker , der im Hintergrund ausgeführt wird. Verwenden Sie den Service Worker, um Hintergrundnachrichten zu verarbeiten.

Erstellen Sie zunächst eine neue Datei in Ihrem web und nennen Sie sie firebase-messaging-sw.js :

importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.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 die App- als auch die Messaging-SDKs importieren, Firebase initialisieren und die messaging Variable verfügbar machen.

Als nächstes muss der Arbeitnehmer registriert werden. Registrieren Sie Ihren Worker in der Eintragsdatei, nachdem die Datei main.dart.js geladen wurde:

<html>
<body>
  ...
  <script src="main.dart.js" type="application/javascript"></script>
  <script>
       if ('serviceWorker' in navigator) {
          // Service workers are supported. Use them.
          window.addEventListener('load', function () {
            // ADD THIS LINE
            navigator.serviceWorker.register('/firebase-messaging-sw.js');

            // 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;

            //  ...
          });
      }
  </script>

Starten Sie als Nächstes Ihre Flutter-Anwendung neu. Der Mitarbeiter wird registriert und alle Hintergrundmeldungen werden über diese Datei verarbeitet.

Umgang mit Interaktion

Da es sich bei Benachrichtigungen um einen sichtbaren Hinweis handelt, interagieren Benutzer häufig mit ihnen (durch Drücken). Das Standardverhalten auf Android und iOS besteht darin, die Anwendung zu öffnen. Wenn die Anwendung beendet wird, wird sie gestartet; Wenn es im Hintergrund ist, wird es in den Vordergrund gebracht.

Abhängig vom Inhalt einer Benachrichtigung möchten Sie möglicherweise die Interaktion des Benutzers beim Öffnen der Anwendung verwalten. Wenn beispielsweise eine neue Chat-Nachricht über eine Benachrichtigung gesendet wird und der Benutzer darauf drückt, möchten Sie möglicherweise die spezifische Konversation öffnen, wenn die Anwendung geöffnet wird.

Das firebase-messaging Paket bietet zwei Möglichkeiten, diese Interaktion zu handhaben:

  • getInitialMessage() : Wenn die Anwendung aus einem beendeten Zustand geöffnet wird, wird ein Future zurückgegeben, der eine RemoteMessage enthält. Sobald die RemoteMessage verbraucht ist, wird sie entfernt.
  • onMessageOpenedApp : Ein Stream , der eine RemoteMessage sendet, wenn die Anwendung aus einem Hintergrundzustand geöffnet wird.

Es wird empfohlen, beide Szenarien zu berücksichtigen, um eine reibungslose Benutzererfahrung für Ihre Benutzer zu gewährleisten. Das folgende Codebeispiel zeigt, wie dies erreicht werden kann:

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 der Interaktion umgehen, hängt von der Einrichtung Ihrer Anwendung ab. Das obige Beispiel zeigt eine grundlegende Illustration mit einem StatefulWidget.

Nachrichten lokalisieren

Sie können lokalisierte Zeichenfolgen auf zwei verschiedene Arten senden:

  • Speichern Sie die bevorzugte Sprache jedes Ihrer Benutzer auf Ihrem Server und senden Sie individuelle Benachrichtigungen für jede Sprache
  • Betten Sie lokalisierte Zeichenfolgen in Ihre App ein und nutzen Sie die nativen Gebietsschemaeinstellungen des Betriebssystems

So verwenden Sie die zweite Methode:

Android

  1. Geben Sie Ihre Nachrichten in der Standardsprache in resources/values/strings.xml an:

    <string name="notification_title">Hello world</string>
    <string name="notification_message">This is a message</string>
    
  2. Geben Sie die übersetzten Nachrichten im Verzeichnis values- language an. Geben Sie beispielsweise französische Nachrichten in resources/values-fr/strings.xml an:

    <string name="notification_title">Bonjour le monde</string>
    <string name="notification_message">C'est un message</string>
    
  3. Verwenden Sie in der Servernutzlast anstelle der Schlüssel title , message und body die Schlüssel title_loc_key und body_loc_key für Ihre lokalisierte Nachricht und legen Sie sie auf das name der Nachricht fest, die Sie anzeigen möchten.

    Die Nachrichtennutzlast würde so aussehen:

    {
      "data": {
        "title_loc_key": "notification_title",
        "body_loc_key": "notification_message"
      }
    }
    

iOS

  1. Geben Sie Ihre Nachrichten in der Standardsprache in Base.lproj/Localizable.strings an:

    "NOTIFICATION_TITLE" = "Hello World";
    "NOTIFICATION_MESSAGE" = "This is a message";
    
  2. Geben Sie die übersetzten Nachrichten im language .lproj an. Geben Sie beispielsweise französische Nachrichten in fr.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"
      }
    }
    

Aktivieren Sie den Export von Nachrichtenübermittlungsdaten

Sie können Ihre Nachrichtendaten zur weiteren Analyse in BigQuery exportieren. Mit BigQuery können Sie die Daten mit BigQuery SQL analysieren, sie zu einem anderen Cloud-Anbieter exportieren oder die Daten für Ihre benutzerdefinierten ML-Modelle verwenden. Ein Export nach BigQuery umfasst alle verfügbaren Daten für Nachrichten, unabhängig vom Nachrichtentyp oder davon, ob die Nachricht über die API oder den Notifications Composer gesendet wird.

Um den Export zu aktivieren, befolgen Sie zunächst die hier beschriebenen Schritte und befolgen Sie dann diese Anweisungen:

Android

Sie können den folgenden Code verwenden:

await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

Für iOS müssen Sie 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

Netz

Für Web müssen Sie Ihren Servicemitarbeiter ändern, um die v9-Version des SDK verwenden zu können. Die v9-Version muss gebündelt werden, Sie müssen also beispielsweise einen Bundler wie esbuild verwenden, damit der Service Worker funktioniert. Sehen Sie sich die Beispiel-App an, um zu sehen, wie Sie dies erreichen können.

Sobald Sie auf das v9 SDK migriert haben, 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 web zu exportieren.

Zeigen Sie Bilder in Benachrichtigungen auf iOS an

Damit eingehende FCM-Benachrichtigungen auf Apple-Geräten Bilder aus der FCM-Nutzlast anzeigen, müssen Sie eine zusätzliche Benachrichtigungsdiensterweiterung hinzufügen und Ihre App für deren Verwendung konfigurieren.

Wenn Sie die Firebase-Telefonauthentifizierung verwenden, müssen Sie den Firebase Auth-Pod zu Ihrer Poddatei hinzufügen.

Schritt 1 – Fügen Sie eine Benachrichtigungsdiensterweiterung hinzu

  1. Klicken Sie in Xcode auf Datei > Neu > Ziel...
  2. Ein Modal präsentiert eine Liste möglicher Ziele. Scrollen Sie nach unten oder verwenden Sie den Filter, um Notification Service Extension auszuwählen. Weiter klicken .
  3. Fügen Sie einen Produktnamen hinzu (verwenden Sie „ImageNotification“, um diesem Tutorial zu folgen), stellen Sie die Sprache auf Objective-C ein und klicken Sie auf „Fertig stellen“ .
  4. Aktivieren Sie das Schema, indem Sie auf Aktivieren klicken.

Schritt 2 – Ziel zur Poddatei hinzufügen

Stellen Sie sicher, dass Ihre neue Erweiterung Zugriff auf den Firebase/Messaging Pod hat, indem Sie sie in der Poddatei hinzufügen:

  1. Öffnen Sie im Navigator die Poddatei: Pods > Poddatei

  2. Scrollen Sie nach unten 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
    
  3. Installieren oder aktualisieren Sie Ihre Pods mit pod install aus dem ios oder macos -Verzeichnis.

Schritt 3 – Verwenden Sie den Erweiterungshelfer

Zu diesem Zeitpunkt sollte noch alles normal laufen. Der letzte Schritt ist das Aufrufen des Erweiterungshelfers.

  1. Wählen Sie im Navigator Ihre ImageNotification-Erweiterung aus

  2. Öffnen Sie die Datei NotificationService.m .

  3. Importieren Sie oben in der Datei FirebaseMessaging.h direkt nach NotificationService.h wie unten gezeigt.

    Ersetzen Sie den Inhalt von NotificationService.m durch:

    #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 – Fügen Sie das Bild zur Nutzlast hinzu

In Ihrer Benachrichtigungsnutzlast können Sie jetzt ein Bild hinzufügen. Informationen zum Erstellen einer Sendeanforderung finden Sie in der iOS-Dokumentation. Beachten Sie, dass das Gerät eine maximale Bildgröße von 300 KB erzwingt.