Odbieranie wiadomości w aplikacji Flutter

W zależności od stanu urządzenia wiadomości przychodzące są obsługiwane w różny sposób. Do oraz jak wdrożyć FCM w swojej aplikacji, należy najpierw określić różne stany urządzenia:

Stan Opis
Pierwszy plan Gdy aplikacja jest otwarta, widoczna i używana.
Informacje ogólne Gdy aplikacja jest otwarta, ale w tle (zminimalizowana). Zwykle dzieje się tak, gdy użytkownik naciśnie przycisk „Ekran główny” przycisk użytkownik przełączył się na inną aplikację, korzystając z przełącznika aplikacji. lub aplikacja jest otwarta na innej karcie (internetowej).
Zakończona Gdy urządzenie jest zablokowane lub aplikacja nie jest uruchomiona.

Jest kilka warunków wstępnych, które muszą zostać spełnione, żeby aplikacja mogła Otrzymywanie ładunków wiadomości przez FCM:

  • Aplikacja musi być otwarta co najmniej raz (aby można było zarejestrować się w FCM).
  • Jeśli użytkownik przesunie aplikację poza przełącznik aplikacji w systemie iOS, trzeba będzie ponownie otworzyć ją ręcznie, aby wiadomości w tle zaczęły działać.
  • Jeśli użytkownik wymusi zamknięcie aplikacji na urządzeniu z Androidem w ustawieniach urządzenia, aby wiadomości zaczęły działać, trzeba ponownie otworzyć ją ręcznie.
  • W przeglądarce musisz wysłać prośbę o token (przy użyciu getToken()) za pomocą certyfikatu Web Push Certificate.

Prośba o zgodę na otrzymywanie wiadomości

w systemie iOS, macOS, w internecie i na Androidzie 13 (lub nowszym), zanim będzie można przekazywać ładunki FCM. na urządzenie, musisz najpierw poprosić użytkownika o zgodę.

Pakiet firebase_messaging zapewnia prosty interfejs API do wysyłania próśb o przyznanie uprawnień za pomocą metody requestPermission. Ten interfejs API akceptuje szereg argumentów nazwanych określających typ uprawnień, których dotyczy żądanie, na przykład wiadomości zawierające ładunki powiadomień mogą aktywować dźwięk lub odczytywać wiadomości za pomocą Siri. Domyślnie że metoda żąda rozsądnych uprawnień domyślnych. Referencyjny interfejs API zawiera pełną dokumentację wyjaśniającą, do czego służą poszczególne uprawnienia.

Aby rozpocząć, wywołaj metodę z aplikacji (na iOS wyświetli się natywna wersja modalna, w przeglądarce) uruchomi się natywny przepływ interfejsu API przeglądarki):

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}');

Właściwość authorizationStatus obiektu NotificationSettings zwrócona z żądanie może posłużyć do określenia ogólnej decyzji użytkownika:

  • authorized: użytkownik przyznał uprawnienie.
  • denied: użytkownik odmówił przyznania uprawnień.
  • notDetermined: użytkownik nie zdecydował jeszcze, czy przyznać uprawnienia.
  • provisional: użytkownik przyznał tymczasowe uprawnienia

Pozostałe właściwości obiektu NotificationSettings pokazują, czy określone uprawnienie jest włączone, wyłączone czy nieobsługiwane w bieżącym urządzenia.

Po otrzymaniu uprawnień i zrozumieniu różnych typów stanu urządzenia aplikacja może zacząć obsługiwać przychodzące Ładunki w FCM.

Obsługa wiadomości

W zależności od bieżącego stanu aplikacji ładunki przychodzące o różnych typy wiadomości ich obsługę wymagają różnych implementacji:

Wiadomości na pierwszym planie

Aby obsługiwać wiadomości, gdy aplikacja działa na pierwszym planie, nasłuchuj strumienia 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}');
  }
});

Strumień zawiera element RemoteMessage, który zawiera szczegółowe informacje różne informacje o ładunku, takie jak pochodzenie, unikalny identyfikator, czas wysłania, powiadomienie i inne informacje. Ponieważ wiadomość została pobrana, gdy Twoja aplikacja działa na pierwszym planie, możesz bezpośrednio uzyskać dostęp do Flutter stanu i kontekstu aplikacji.

Wiadomości na pierwszym planie i wiadomości z powiadomieniami

Wiadomości z powiadomieniami przychodzące, gdy aplikacja działa na pierwszym planie, domyślnie nie będą wyświetlać widocznego powiadomienia w obu przypadkach. na Androidzie i iOS. Można jednak zastąpić to działanie:

  • Na Androidzie musisz utworzyć „Wysoki priorytet”. kanału powiadomień.
  • W systemie iOS można zaktualizować opcje prezentacji w aplikacji.

Wiadomości w tle

Proces obsługi wiadomości w tle różni się w zależności od wersji natywnej (Android Apple) i platform internetowych.

Platformy Apple i Android

Obsługuj wiadomości w tle przez zarejestrowanie modułu obsługi onBackgroundMessage. Po odebraniu wiadomości izolowanie jest wywoływane (tylko na Androidzie, iOS/macOS nie wymaga osobnego izolacji), co pozwala obsługiwać wiadomości nawet wtedy, gdy aplikacja nie jest uruchomiona.

Kilka kwestii, o których warto pamiętać w przypadku modułu obsługi wiadomości w tle:

  1. Nie może to być funkcja anonimowa.
  2. Musi to być funkcja najwyższego poziomu (np. nie jest to metoda klasy, która wymaga inicjowania).
  3. Jeśli używasz Flutter w wersji 3.3.0 lub nowszej, moduł obsługi wiadomości musi mieć tuż nad deklaracją funkcji adnotację @pragma('vm:entry-point') (w przeciwnym razie może zostać usunięta podczas potrząsania drzewem w trybie wydania).
@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());
}

Ponieważ moduł obsługi działa w własnym izolacji poza kontekstem aplikacji, nie można zaktualizować stanu aplikacji ani wykonywania interfejsu użytkownika wpływającego na logikę. Możesz jednak wykonywać logikę, np. żądania HTTP, wykonywać operacje wejścia-wyjścia (np. aktualizacja pamięci lokalnej), komunikacja z innymi wtyczkami itp.

Warto też jak najszybciej dokończyć obliczenia. Długie, intensywne zadania wpływają na wydajność urządzenia i może spowodować zakończenie tego procesu przez system operacyjny. Jeśli zadania trwają dłużej niż 30 sekund, urządzenie może automatycznie zakończyć proces.

Sieć

W przeglądarce napisz skrypt JavaScript Service Worker, który działa w tle. Skrypt service worker do obsługi wiadomości w tle.

Na początek utwórz nowy plik w katalogu web i nazwij go 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);
});

Plik musi zaimportować zarówno pakiet SDK aplikacji, jak i pakiety SDK do przesyłania wiadomości, zainicjować Firebase i udostępnić zmienną messaging.

Następnie należy zarejestrować pracownika. Po załadowaniu pliku main.dart.js zarejestruj w pliku wpisu Twoją instancję roboczą:

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

Następnie ponownie uruchom aplikację Flutter. Zasób roboczy zostanie zarejestrowany, a wszystkie wiadomości w tle będą obsługiwane za pomocą tego pliku.

Radzenie sobie z interakcją

Powiadomienia to widoczna wskazówka, więc użytkownicy często wchodzą z nimi w interakcję (naciskając je). Na Androidzie i iOS domyślnie otwiera się aplikacji. Jeśli aplikacja zostanie zakończona, zostanie uruchomiona. jeśli film znajdzie się w tle, zostanie przesunięty na pierwszy plan.

W zależności od treści powiadomienia możesz zdecydować się na interakcję użytkownika z aplikacją, gdy zostanie otwarta aplikacja. Jeśli na przykład nowa wiadomość na czacie zostanie wysłana przez powiadomienie, a użytkownik je kliknie, możesz otworzyć określony wątek po otwarciu aplikacji.

Pakiet firebase-messaging udostępnia 2 sposoby obsługi tej interakcji:

  • getInitialMessage(): jeśli aplikacja zostanie otwarta w stanie zamknięcia, zostanie zwrócony element Future zawierający RemoteMessage. Po ich użyciu RemoteMessage zostanie usunięty.
  • onMessageOpenedApp: Stream, który publikuje RemoteMessage, gdy aplikacja jest otwarta w tle.

Zalecamy uwzględnienie obu scenariuszy, aby zapewnić użytkownikom bezproblemowe wrażenia. Oto przykładowy kod, który opisuje, jak to zrobić:

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("...");
  }
}

Sposób obsługi interakcji zależy od konfiguracji aplikacji. Powyższy przykład przedstawia podstawową ilustrację za pomocą widżetu StatefulWidget.

Zlokalizuj wiadomości

Zlokalizowane ciągi tekstowe możesz wysyłać na 2 sposoby:

  • Przechowuj preferowany język każdego użytkownika na serwerze i wysyłaj spersonalizowane powiadomienia dla każdego języka
  • Umieść w aplikacji zlokalizowane ciągi znaków i wykorzystaj ustawienia regionalne systemu operacyjnego.

Aby skorzystać z drugiej metody:

Android

  1. Określ wiadomości w domyślnym języku w języku resources/values/strings.xml:

    <string name="notification_title">Hello world</string>
    <string name="notification_message">This is a message</string>
    
  2. Określ przetłumaczone wiadomości w katalogu values-language. Na przykład wiadomości w języku francuskim w resources/values-fr/strings.xml:

    <string name="notification_title">Bonjour le monde</string>
    <string name="notification_message">C'est un message</string>
    
  3. W ładunku serwera zamiast kluczy title, message i body użyj title_loc_key i body_loc_key w przypadku zlokalizowanej wiadomości i ustaw je na atrybut name wiadomości, którą chcesz wyświetlić.

    Wiadomość będzie wyglądać tak:

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

iOS

  1. Określ wiadomości w domyślnym języku w języku Base.lproj/Localizable.strings:

    "NOTIFICATION_TITLE" = "Hello World";
    "NOTIFICATION_MESSAGE" = "This is a message";
    
  2. Określ przetłumaczone wiadomości w katalogu language.lproj. Na przykład wiadomości w języku francuskim w fr.lproj/Localizable.strings:

    "NOTIFICATION_TITLE" = "Bonjour le monde";
    "NOTIFICATION_MESSAGE" = "C'est un message";
    

    Wiadomość będzie wyglądać tak:

    {
      "data": {
        "title_loc_key": "NOTIFICATION_TITLE",
        "body_loc_key": "NOTIFICATION_MESSAGE"
      }
    }
    

Włącz eksportowanie danych dotyczących dostarczania wiadomości

Dane dotyczące wiadomości możesz wyeksportować do BigQuery, aby je dokładniej przeanalizować. BigQuery umożliwia analizowanie danych za pomocą BigQuery SQL, wyeksportuj je do innego dostawcy chmury lub użyj danych na potrzeby niestandardowych modeli ML. Eksport do BigQuery obejmuje wszystkie dostępne dane dotyczące wiadomości niezależnie od typu wiadomości i tego, czy jest ona wysyłana przez za pomocą interfejsu API lub narzędzia do tworzenia powiadomień.

Aby włączyć eksportowanie, najpierw wykonaj czynności opisane tutaj, wykonaj te czynności:

Android

Możesz użyć tego kodu:

await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

W iOS musisz zmienić AppDelegate.m o następujące treści.

#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

Sieć

Jeśli chcesz używać pakietu SDK w wersji 9, musisz zmienić skrypt service worker. Wersja 9 musi być dodana do pakietu, więc musisz użyć na przykład programu do tworzenia pakietów, takiego jak esbuild. uruchomienia skryptu service worker. Aby dowiedzieć się, jak to zrobić, zapoznaj się z przykładową aplikacją.

Po przejściu na pakiet SDK w wersji 9 możesz używać tego kodu:

import {
  experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
  getMessaging,
} from 'firebase/messaging/sw';
...

const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);

Nie zapomnij uruchomić yarn build, aby wyeksportować nową wersję skryptu service worker do folderu web.

Wyświetlanie obrazów w powiadomieniach na urządzeniach z iOS

Jeśli na urządzeniach Apple chcesz, aby przychodzące powiadomienia FCM wyświetlały obrazy z ładunku FCM, musisz dodać dodatkowe rozszerzenie usługi powiadomień i skonfigurować korzystanie z niego w aplikacji.

Jeśli korzystasz z uwierzytelniania Firebase przez telefon, musisz dodać poda uwierzytelniania Firebase do pliku Podfile.

Krok 1. Dodaj rozszerzenie usługi powiadomień

  1. W Xcode kliknij File > (Plik >). Nowe > Cel...
  2. Okno modalne zawiera listę możliwych celów. przewiń w dół lub użyj filtra, aby wybrać Rozszerzenie usługi powiadomień. Kliknij Dalej.
  3. Dodaj nazwę produktu (aby kontynuować pracę z samouczkiem, użyj elementu „Image Notification”), ustaw język na Objective-C i kliknij Zakończ.
  4. Włącz schemat, klikając Aktywuj.

Krok 2. Dodaj miejsce docelowe do pliku Podfile

Upewnij się, że nowe rozszerzenie ma dostęp do poda Firebase/Messaging, dodając je w pliku Podfile:

  1. W nawigatorze otwórz plik Podfile: Pods > Plik Podfile

  2. Przewiń plik w dół i dodaj:

    target 'ImageNotification' do
      use_frameworks!
      pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication
      pod 'Firebase/Messaging'
    end
    
  3. Zainstaluj lub zaktualizuj pody za pomocą polecenia pod install z katalogu ios lub macos.

Krok 3. Użyj rozszerzenia

Na razie wszystko powinno działać normalnie. Ostatnim krokiem jest wywołanie asystenta rozszerzenia.

  1. W nawigatorze wybierz rozszerzenie ImagePowiadomienie.

  2. Otwórz plik NotificationService.m.

  3. Na górze pliku zaimportuj FirebaseMessaging.h tuż za NotificationService.h, jak pokazano poniżej.

    Zamień zawartość pliku NotificationService.m na:

    #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
    

Krok 4. Dodaj obraz do ładunku

Do ładunku powiadomień możesz teraz dodać obraz. Informacje o tworzeniu próśb o wysłanie wiadomości znajdziesz w dokumentacji iOS. Pamiętaj, że na urządzeniu obowiązuje limit rozmiaru obrazu 300 KB.