Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Odbieraj wiadomości w aplikacji Flutter

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

W zależności od stanu urządzenia wiadomości przychodzące są obsługiwane w różny sposób. Aby zrozumieć te scenariusze i jak zintegrować FCM z własną aplikacją, należy najpierw ustalić różne stany, w jakich może znajdować się urządzenie:

Państwo Opis
Pierwszoplanowy Gdy aplikacja jest otwarta, widoczna i używana.
Tło Gdy aplikacja jest otwarta, ale w tle (zminimalizowana). Zwykle dzieje się tak, gdy użytkownik nacisnął przycisk „Home” na urządzeniu, przełączył się do innej aplikacji za pomocą przełącznika aplikacji lub otworzył aplikację w innej karcie (internecie).
Zakończony Gdy urządzenie jest zablokowane lub aplikacja nie jest uruchomiona.

Istnieje kilka warunków wstępnych, które muszą zostać spełnione, aby aplikacja mogła odbierać ładunki wiadomości za pośrednictwem FCM:

  • Aplikacja musi być co najmniej raz otwarta (aby umożliwić rejestrację w FCM).
  • W systemie iOS, jeśli użytkownik przesunie aplikację z przełącznika aplikacji, należy ją ponownie otworzyć ręcznie, aby komunikaty w tle zaczęły ponownie działać.
  • W systemie Android, jeśli użytkownik wymusza zamknięcie aplikacji w ustawieniach urządzenia, należy ją ponownie otworzyć ręcznie, aby wiadomości zaczęły działać.
  • W sieci musisz poprosić o token (za pomocą getToken() ) za pomocą certyfikatu web push.

Poproś o pozwolenie na otrzymywanie wiadomości (Apple i Web)

W systemach iOS, macOS i sieci Web, zanim ładunki FCM będą mogły zostać odebrane na Twoje urządzenie, musisz najpierw poprosić użytkownika o zgodę.

Pakiet firebase_messaging udostępnia prosty interfejs API do żądania uprawnień za pomocą metody requestPermission . Ten interfejs API akceptuje szereg nazwanych argumentów, które definiują typ uprawnień, o które chcesz poprosić, na przykład czy wiadomości zawierające ładunki powiadomień mogą wyzwalać dźwięk lub odczytywać wiadomości za pośrednictwem Siri. Domyślnie metoda żąda rozsądnych uprawnień domyślnych. Referencyjny interfejs API zawiera pełną dokumentację dotyczącą tego, do czego służy każde uprawnienie.

Aby rozpocząć, wywołaj metodę z aplikacji (w systemie iOS zostanie wyświetlony natywny modalny, w sieci zostanie uruchomiony natywny przepływ 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}');

NotificationSettings authorizationStatus z żądania może służyć do określenia ogólnej decyzji użytkownika:

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

Inne właściwości NotificationSettings zwracają, czy określone uprawnienie jest włączone, wyłączone lub nieobsługiwane na bieżącym urządzeniu.

Po udzieleniu pozwolenia i zrozumieniu różnych typów stanu urządzenia aplikacja może teraz rozpocząć obsługę przychodzących ładunków FCM.

Obsługa wiadomości

W zależności od bieżącego stanu aplikacji przychodzące ładunki różnych typów wiadomości wymagają różnych implementacji do ich obsługi:

Wiadomości na pierwszym planie

Aby obsługiwać wiadomości, gdy aplikacja jest na pierwszym planie, słuchaj 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 RemoteMessage , wyszczególniającą różne informacje o ładunku, takie jak skąd pochodzi, unikalny identyfikator, czas wysłania, czy zawiera powiadomienie i inne. Ponieważ wiadomość została pobrana, gdy aplikacja jest na pierwszym planie, możesz bezpośrednio uzyskać dostęp do stanu i kontekstu aplikacji Flutter.

Wiadomości na pierwszym planie i powiadomienia

Powiadomienia przychodzące, gdy aplikacja jest na pierwszym planie, domyślnie nie wyświetlają widocznego powiadomienia, zarówno w systemie Android, jak i iOS. Możliwe jest jednak zignorowanie tego zachowania:

  • W systemie Android musisz utworzyć kanał powiadomień o wysokim priorytecie.
  • W systemie iOS możesz zaktualizować opcje prezentacji aplikacji.

Wiadomości w tle

Proces obsługi wiadomości w tle różni się na platformach natywnych (Android i Apple) i internetowych.

Platformy Apple i Android

Obsługuj komunikaty w tle, rejestrując procedurę obsługi onBackgroundMessage . Po odebraniu wiadomości pojawia się izolacja (tylko Android, iOS/macOS nie wymaga oddzielnej izolacji), umożliwiając obsługę wiadomości nawet wtedy, gdy aplikacja nie jest uruchomiona.

Należy pamiętać o kilku kwestiach dotyczących obsługi wiadomości w tle:

  1. Nie może to być funkcja anonimowa.
  2. Musi to być funkcja najwyższego poziomu (np. nie metoda klasy, która wymaga inicjalizacji).
  3. Musi być opatrzony adnotacją @pragma('vm:entry-point') tuż nad deklaracją funkcji (w przeciwnym razie może zostać usunięty podczas potrząsania drzewem w trybie zwolnienia).
@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ż program obsługi działa we własnym izolowaniu poza kontekstem aplikacji, nie można zaktualizować stanu aplikacji ani wykonać żadnej logiki wpływającej na interfejs użytkownika. Możesz jednak wykonywać logikę, taką jak żądania HTTP, wykonywać operacje IO (np. aktualizować pamięć lokalną), komunikować się z innymi wtyczkami itp.

Zaleca się również jak najszybsze uzupełnienie logiki. Wykonywanie długich, intensywnych zadań wpływa na wydajność urządzenia i może spowodować zakończenie procesu przez system operacyjny. Jeśli zadania trwają dłużej niż 30 sekund, urządzenie może automatycznie zakończyć proces.

Sieć

W sieci napisz JavaScript Service Worker , który działa w tle. Użyj pracownika serwisu do obsługi komunikatów w tle.

Aby rozpocząć, 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 importować pakiety SDK aplikacji i wiadomości, inicjować Firebase i udostępniać zmienną messaging .

Następnie pracownik musi zostać zarejestrowany. W pliku wejściowym, po załadowaniu pliku main.dart.js , zarejestruj swojego pracownika:

<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 uruchom ponownie aplikację Flutter. Pracownik zostanie zarejestrowany, a wszelkie wiadomości w tle będą obsługiwane za pośrednictwem tego pliku.

Obsługa interakcji

Ponieważ powiadomienia są widoczną wskazówką, użytkownicy często wchodzą z nimi w interakcję (przez naciśnięcie). Domyślnym zachowaniem zarówno w systemie Android, jak i iOS jest otwarcie aplikacji. Jeśli aplikacja zostanie zamknięta, zostanie uruchomiona; jeśli jest w tle, zostanie przeniesiony na pierwszy plan.

W zależności od treści powiadomienia, możesz chcieć obsłużyć interakcję użytkownika podczas otwierania aplikacji. Na przykład, jeśli nowa wiadomość czatu zostanie wysłana za pośrednictwem powiadomienia, a użytkownik ją naciśnie, możesz chcieć otworzyć konkretną rozmowę po otwarciu aplikacji.

Pakiet firebase-messaging zapewnia dwa sposoby obsługi tej interakcji:

  • getInitialMessage() : Jeśli aplikacja zostanie otwarta ze stanu zakończenia, zostanie zwrócona Future zawierająca RemoteMessage . Po zużyciu RemoteMessage zostanie usunięty.
  • onMessageOpenedApp : Stream , który publikuje RemoteMessage , gdy aplikacja jest otwierana ze stanu tła.

Zaleca się obsługę obu scenariuszy, aby zapewnić użytkownikom płynny UX. Poniższy przykład kodu pokazuje, jak można to osiągnąć:

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ę przy użyciu StatefulWidget.

Zlokalizuj wiadomości

Zlokalizowane ciągi można wysyłać na dwa różne sposoby:

  • Przechowuj preferowany język każdego z Twoich użytkowników na swoim serwerze i wysyłaj dostosowane powiadomienia dla każdego języka
  • Osadź zlokalizowane ciągi znaków w swojej aplikacji i korzystaj z natywnych ustawień regionalnych systemu operacyjnego

Oto jak użyć drugiej metody:

Android

  1. Określ wiadomości w domyślnym języku w 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 komunikaty w katalogu values- language . Na przykład podaj francuskie komunikaty 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 używać kluczy title , message i body , użyj title_loc_key i body_loc_key dla zlokalizowanej wiadomości i ustaw je na atrybut name wiadomości, którą chcesz wyświetlić.

    Ładunek wiadomości wyglądałby tak:

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

iOS

  1. Określ wiadomości w domyślnym języku w 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 podaj francuskie wiadomości w fr.lproj/Localizable.strings :

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

    Ładunek wiadomości wyglądałby tak:

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

Włącz eksport danych dostarczania wiadomości

Możesz wyeksportować dane wiadomości do BigQuery w celu dalszej analizy. BigQuery pozwala analizować dane za pomocą BigQuery SQL, eksportować je do innego dostawcy chmury lub używać danych do niestandardowych modeli ML. Eksport do BigQuery obejmuje wszystkie dostępne dane dotyczące wiadomości, niezależnie od typu wiadomości i tego, czy wiadomość jest wysyłana za pośrednictwem interfejsu API czy kompozytora powiadomień.

Aby włączyć eksport, najpierw wykonaj czynności opisane tutaj , a następnie postępuj zgodnie z poniższymi instrukcjami:

Android

Możesz użyć następującego kodu: dart await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

W przypadku iOS musisz zmienić AppDelegate.m na następującą zawartość.

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

W przypadku sieci Web musisz zmienić pracownika usługi, aby korzystać z wersji v9 pakietu SDK. Wersja v9 musi być spakowana, więc musisz użyć pakietu, takiego jak esbuild , na przykład, aby umożliwić Service Workerowi pracę. Zobacz przykładową aplikację , aby zobaczyć, jak to osiągnąć.

Po migracji do pakietu SDK v9 możesz użyć następującego kodu:

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

...

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

Nie zapomnij uruchomić yarn build , aby wyeksportować nową wersję pracownika serwisu do folderu web .