Firebase is back at Google I/O on May 10! Register now

Empfangen Sie Nachrichten in einer Flutter-App

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Je nach Zustand eines Geräts werden eingehende Nachrichten unterschiedlich behandelt. Um diese Szenarien zu verstehen und FCM in Ihre eigene Anwendung zu integrieren, ist es zunächst wichtig, die verschiedenen Zustände festzulegen, 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, aber im Hintergrund (minimiert). Dies tritt normalerweise auf, 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.

Es gibt einige Voraussetzungen, die erfüllt sein müssen, bevor die Anwendung Nachrichtennutzlasten über FCM empfangen kann:

  • Die Anwendung muss mindestens einmal geöffnet worden sein (um die 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.

Berechtigung zum Empfangen von Nachrichten anfordern (Apple und Web)

Unter iOS, macOS und Web müssen Sie zuerst die Erlaubnis des Benutzers einholen, bevor FCM-Payloads auf Ihrem Gerät empfangen werden können.

Das firebase_messaging -Paket bietet eine einfache API zum Anfordern von Berechtigungen über die Methode „ requestPermission “. Diese API akzeptiert eine Reihe von benannten Argumenten, 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, wofür jede Berechtigung vorgesehen ist.

Rufen Sie zunächst die Methode aus Ihrer Anwendung auf (unter iOS wird ein natives Modal angezeigt, im Web wird der native API-Fluss 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 Arten von Gerätestatus verstanden wurden, kann Ihre Anwendung nun damit beginnen, die eingehenden FCM-Nutzlasten zu verarbeiten.

Nachrichtenbehandlung

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

Vordergrundmeldungen

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

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 , die verschiedene Informationen über die Nutzlast enthält, 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 war, können Sie direkt auf den Status und Kontext Ihrer Flutter-Anwendung zugreifen.

Vordergrund- und Benachrichtigungsmeldungen

Benachrichtigungen, die eintreffen, während sich die Anwendung im Vordergrund befindet, zeigen standardmäßig keine sichtbare Benachrichtigung an, sowohl auf Android als auch auf iOS. 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 Prozess der Verarbeitung von Hintergrundnachrichten ist auf nativen (Android und Apple) und webbasierten Plattformen unterschiedlich.

Apple-Plattformen und Android

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

Es gibt ein paar Dinge, die Sie bei Ihrem Hintergrundnachrichten-Handler beachten sollten:

  1. Es darf keine anonyme Funktion sein.
  2. Es muss eine Top-Level-Funktion sein (z. B. keine Klassenmethode, die eine Initialisierung erfordert).
  3. Es muss direkt über der Funktionsdeklaration mit @pragma('vm:entry-point') annotiert werden (andernfalls kann es 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 seinem 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-Anforderungen ausführen, IO-Operationen ausführen (z. B. lokalen Speicher aktualisieren), mit anderen Plugins kommunizieren usw.

Es wird auch 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 beendet. Wenn Aufgaben länger als 30 Sekunden ausgeführt werden, beendet das Gerät den Prozess möglicherweise automatisch.

Netz

Schreiben Sie im Web einen JavaScript Service Worker , der im Hintergrund läuft. Verwenden Sie den Service Worker, um Hintergrundnachrichten zu bearbeiten.

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 Arbeiter wird registriert und alle Hintergrundnachrichten werden über diese Datei abgewickelt.

Umgang mit Interaktion

Da Benachrichtigungen ein sichtbarer Hinweis sind, ist es üblich, dass Benutzer mit ihnen interagieren (durch Drücken). Das Standardverhalten auf Android und iOS ist das Öffnen der Anwendung. 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 verarbeiten, wenn die Anwendung geöffnet wird. Wenn beispielsweise eine neue Chat-Nachricht über eine Benachrichtigung gesendet wird und der Benutzer darauf drückt, möchten Sie möglicherweise die bestimmte 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, das eine RemoteMessage enthält. Einmal verbraucht, wird die RemoteMessage entfernt.
  • onMessageOpenedApp : Ein Stream , der eine RemoteMessage wenn die Anwendung aus einem Hintergrundzustand geöffnet wird.

Es wird empfohlen, beide Szenarien zu behandeln, um eine reibungslose UX 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 Ihrer Anwendungskonfiguration 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 benutzerdefinierte 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 . 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 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 wie folgt 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 wie folgt 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 Benachrichtigungs-Composer gesendet wird.

Um den Export zu aktivieren, befolgen Sie zuerst die hier beschriebenen Schritte und folgen Sie dann diesen Anweisungen:

Android

Sie können den folgenden Code verwenden: dart await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

Für iOS müssen Sie die 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 das Web müssen Sie Ihren Service Worker ändern, um die v9-Version des SDK zu verwenden. Die v9-Version muss gebündelt werden, also müssen Sie beispielsweise einen Bundler wie esbuild verwenden, um den Service Worker zum Laufen zu bringen. Sehen Sie sich die Beispiel-App an, um zu sehen, wie Sie dies erreichen.

Nach der Migration zum v9 SDK 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.