Flutter uygulamasında mesaj alma

Cihazın durumuna bağlı olarak gelen mesajlar farklı şekilde işlenir. Bu senaryoları ve FCM'yi kendi uygulamanıza nasıl entegre edeceğinizi anlamak için öncelikle bir cihazın bulunabileceği çeşitli durumları belirlemek önemlidir:

Durum Tanım
Ön plan Uygulama açıkken, görünürken ve kullanımdayken.
Arka plan Uygulama açıkken, ancak arka planda (simge durumuna küçültülmüş). Bu genellikle kullanıcı cihazdaki "ana sayfa" düğmesine bastığında, uygulama değiştiriciyi kullanarak başka bir uygulamaya geçtiğinde veya uygulamayı farklı bir sekmede (web) açtığında meydana gelir.
Sonlandırılmış Cihaz kilitliyken veya uygulama çalışmıyorken.

Uygulamanın FCM aracılığıyla mesaj yüklerini alabilmesi için karşılanması gereken birkaç önkoşul vardır:

  • Başvurunun en az bir kez açılmış olması gerekir (FCM'ye kayıt yapılabilmesi için).
  • iOS'ta kullanıcı uygulamayı uygulama değiştiriciden kaydırırsa arka plan mesajlarının yeniden çalışmaya başlaması için uygulamanın manuel olarak yeniden açılması gerekir.
  • Android'de kullanıcı cihaz ayarlarından uygulamadan çıkmaya zorlanırsa mesajların çalışmaya başlaması için uygulamanın manuel olarak yeniden açılması gerekir.
  • Web'de, web push sertifikanızla birlikte bir belirteç ( getToken() kullanarak) talep etmiş olmanız gerekir.

Mesaj almak için izin isteyin

iOS, macOS, web ve Android 13'te (veya daha yenisinde), FCM verisinin cihazınıza alınabilmesi için öncelikle kullanıcının iznini istemeniz gerekir.

firebase_messaging paketi, requestPermission yöntemi aracılığıyla izin istemek için basit bir API sağlar. Bu API, talep etmek istediğiniz izinlerin türünü tanımlayan bir dizi adlandırılmış bağımsız değişkeni kabul eder; örneğin, bildirim verileri içeren mesajların bir sesi tetikleyip tetikleyemeyeceği veya Siri aracılığıyla mesajları sesli okuyabileceği gibi. Varsayılan olarak, yöntem makul varsayılan izinler ister. Referans API, her bir iznin ne için olduğuna ilişkin tam belgeler sağlar.

Başlamak için uygulamanızdan yöntemi çağırın (iOS'ta yerel bir model görüntülenecek, web'de tarayıcının yerel API akışı tetiklenecektir):

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

İstekten döndürülen NotificationSettings nesnesinin authorizationStatus özelliği, kullanıcının genel kararını belirlemek için kullanılabilir:

  • authorized : Kullanıcı izin verdi.
  • denied : Kullanıcı izni reddetti.
  • notDetermined : Kullanıcı henüz izin verilip verilmeyeceğini seçmedi.
  • provisional : Kullanıcıya geçici izin verildi

NotificationSettings diğer özellikler, belirli bir iznin geçerli cihazda etkinleştirilip etkinleştirilmediğini, devre dışı bırakıldığını veya desteklenmediğini döndürür.

İzin verildikten ve farklı cihaz durumu türleri anlaşıldıktan sonra uygulamanız artık gelen FCM verilerini işlemeye başlayabilir.

Mesaj işleme

Uygulamanızın mevcut durumuna bağlı olarak, farklı mesaj türlerinden gelen veri yüklerinin işlenmesi için farklı uygulamalar gerekir:

Ön plan mesajları

Uygulamanız ön plandayken mesajları yönetmek için onMessage akışını dinleyin.

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

Akış, yük hakkında nereden geldiği, benzersiz kimlik, gönderilme zamanı, bir bildirim içerip içermediği ve daha fazlası gibi çeşitli bilgileri ayrıntılandıran bir RemoteMessage içerir. Mesaj, uygulamanız ön plandayken alındığından, Flutter uygulamanızın durumuna ve içeriğine doğrudan erişebilirsiniz.

Ön plan ve Bildirim mesajları

Uygulama ön plandayken gelen bildirim mesajları hem Android hem de iOS'ta varsayılan olarak görünür bir bildirim göstermez. Ancak bu davranışı geçersiz kılmak mümkündür:

  • Android'de "Yüksek Öncelikli" bir bildirim kanalı oluşturmanız gerekir.
  • iOS'ta uygulamanın sunum seçeneklerini güncelleyebilirsiniz.

Arka plan mesajları

Arka plan mesajlarını işleme süreci yerel (Android ve Apple) ve web tabanlı platformlarda farklıdır.

Apple platformları ve Android

Bir onBackgroundMessage işleyicisini kaydederek arka plan mesajlarını işleyin. Mesajlar alındığında, bir izolasyon oluşturulur (yalnızca Android, iOS/macOS ayrı bir izolasyon gerektirmez), uygulamanız çalışmıyorken bile mesajları yönetmenize olanak tanır.

Arka plan mesaj işleyiciniz hakkında aklınızda bulundurmanız gereken birkaç nokta vardır:

  1. Anonim bir işlev olmamalıdır.
  2. Üst düzey bir işlev olmalıdır (örn. başlatma gerektiren bir sınıf yöntemi değil).
  3. Flutter 3.3.0 veya üstünü kullanırken, mesaj işleyicisine fonksiyon bildiriminin hemen üstüne @pragma('vm:entry-point') ek açıklaması eklenmelidir (aksi takdirde serbest bırakma modu için ağaç sallama sırasında kaldırılabilir).
@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());
}

İşleyici, uygulama bağlamınızın dışında kendi izolasyonunda çalıştığından, uygulama durumunu güncellemek veya kullanıcı arayüzünü etkileyen herhangi bir mantığı yürütmek mümkün değildir. Bununla birlikte, HTTP istekleri gibi mantıkları gerçekleştirebilir, IO işlemlerini gerçekleştirebilir (örn. yerel depolamayı güncelleme), diğer eklentilerle iletişim kurabilirsiniz vb.

Ayrıca mantığınızı en kısa sürede tamamlamanız tavsiye edilir. Uzun, yoğun görevlerin çalıştırılması cihazın performansını etkiler ve işletim sisteminin süreci sonlandırmasına neden olabilir. Görevler 30 saniyeden uzun sürerse cihaz işlemi otomatik olarak sonlandırabilir.

Web'de arka planda çalışan bir JavaScript Hizmet Çalışanı yazın. Arka plan mesajlarını işlemek için hizmet çalışanını kullanın.

Başlamak için web dizininizde yeni bir dosya oluşturun ve onu firebase-messaging-sw.js olarak adlandırın:

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

Dosyanın hem uygulamayı hem de mesajlaşma SDK'larını içe aktarması, Firebase'i başlatması ve messaging değişkenini göstermesi gerekir.

Daha sonra işçinin kayıt olması gerekmektedir. main.dart.js dosyası yüklendikten sonra giriş dosyasında çalışanınızı kaydedin:

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

Daha sonra Flutter uygulamanızı yeniden başlatın. Çalışan kaydedilecek ve tüm arka plan mesajları bu dosya aracılığıyla ele alınacaktır.

Etkileşimi Yönetme

Bildirimler görünür bir ipucu olduğundan, kullanıcıların bildirimlerle etkileşime girmesi (basarak) yaygındır. Hem Android hem de iOS'taki varsayılan davranış, uygulamayı açmaktır. Başvurunun sonlandırılması halinde başlatılacak; arka planda ise ön plana çıkarılacaktır.

Bir bildirimin içeriğine bağlı olarak, uygulama açıldığında kullanıcının etkileşimini yönetmek isteyebilirsiniz. Örneğin, bir bildirim yoluyla yeni bir sohbet mesajı gönderilirse ve kullanıcı bu mesaja basarsa, uygulama açıldığında söz konusu sohbeti açmak isteyebilirsiniz.

firebase-messaging paketi bu etkileşimi yönetmenin iki yolunu sunar:

  • getInitialMessage() : Uygulama sonlandırılmış bir durumdan açılırsa RemoteMessage içeren bir Future döndürülür. Kullanıldıktan sonra RemoteMessage kaldırılacaktır.
  • onMessageOpenedApp : Uygulama bir arka plan durumundan açıldığında RemoteMessage gönderen bir Stream .

Kullanıcılarınız için sorunsuz bir kullanıcı deneyimi sağlamak amacıyla her iki senaryonun da ele alınması önerilir. Aşağıdaki kod örneği bunun nasıl başarılabileceğini özetlemektedir:

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

Etkileşimi nasıl ele alacağınız uygulama kurulumunuza bağlıdır. Yukarıdaki örnekte StatefulWidget'ın kullanıldığı temel bir örnek gösterilmektedir.

Mesajları Yerelleştirin

Yerelleştirilmiş dizeleri iki farklı şekilde gönderebilirsiniz:

  • Kullanıcılarınızın her birinin tercih ettiği dili sunucunuzda saklayın ve her dil için özelleştirilmiş bildirimler gönderin
  • Uygulamanıza yerelleştirilmiş dizeler ekleyin ve işletim sisteminin yerel yerel ayarlarından yararlanın

İkinci yöntemin nasıl kullanılacağı aşağıda açıklanmıştır:

Android

  1. resources/values/strings.xml dosyasında varsayılan dil mesajlarınızı belirtin:

    <string name="notification_title">Hello world</string>
    <string name="notification_message">This is a message</string>
    
  2. values- language dizininde çevrilmiş mesajları belirtin. Örneğin, resources/values-fr/strings.xml dosyasında Fransızca mesajları belirtin:

    <string name="notification_title">Bonjour le monde</string>
    <string name="notification_message">C'est un message</string>
    
  3. Sunucu verisinde, yerelleştirilmiş mesajınız için title , message ve body anahtarlarını kullanmak yerine title_loc_key ve body_loc_key kullanın ve bunları, görüntülemek istediğiniz mesajın name niteliğine ayarlayın.

    Mesaj yükü şu şekilde görünecektir:

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

iOS

  1. Base.lproj/Localizable.strings dosyasında varsayılan dil mesajlarınızı belirtin:

    "NOTIFICATION_TITLE" = "Hello World";
    "NOTIFICATION_MESSAGE" = "This is a message";
    
  2. language .lproj dizininde çevrilmiş mesajları belirtin. Örneğin, Fransızca mesajları fr.lproj/Localizable.strings dosyasında belirtin:

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

    Mesaj yükü şu şekilde görünecektir:

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

İleti teslimi verilerini dışa aktarmayı etkinleştir

Daha ayrıntılı analiz için mesaj verilerinizi BigQuery'ye aktarabilirsiniz. BigQuery, verileri BigQuery SQL kullanarak analiz etmenize, başka bir bulut sağlayıcıya aktarmanıza veya verileri özel makine öğrenimi modelleriniz için kullanmanıza olanak tanır. BigQuery'ye dışa aktarma, mesaj türüne veya mesajın API ya da Bildirimler oluşturucu aracılığıyla gönderilip gönderilmediğine bakılmaksızın mesajlara ilişkin mevcut tüm verileri içerir.

Dışa aktarmayı etkinleştirmek için önce burada açıklanan adımları izleyin, ardından şu talimatları izleyin:

Android

Aşağıdaki kodu kullanabilirsiniz:

await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

İOS için AppDelegate.m dosyasını aşağıdaki içerikle değiştirmeniz gerekir.

#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 için SDK'nın v9 sürümünü kullanabilmek için hizmet çalışanınızı değiştirmeniz gerekir. V9 sürümünün paketlenmesi gerekiyor; bu nedenle, örneğin hizmet çalışanının çalışmasını sağlamak için esbuild gibi bir paketleyici kullanmanız gerekir. Bunu nasıl başaracağınızı görmek için örnek uygulamaya bakın.

v9 SDK'ya geçiş yaptıktan sonra aşağıdaki kodu kullanabilirsiniz:

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

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

Service Worker'ınızın yeni sürümünü web klasörüne aktarmak için yarn build çalıştırmayı unutmayın.

Görselleri iOS'taki bildirimlerde görüntüleyin

Apple cihazlarda, gelen FCM Bildirimlerinin FCM verisinden görseller göstermesi için ek bir bildirim hizmeti uzantısı eklemeniz ve uygulamanızı bunu kullanacak şekilde yapılandırmanız gerekir.

Firebase telefon kimlik doğrulamasını kullanıyorsanız Firebase Auth bölmesini Pod dosyanıza eklemeniz gerekir.

1. Adım - Bir bildirim hizmeti uzantısı ekleyin

  1. Xcode'da Dosya > Yeni > Hedef... öğesine tıklayın.
  2. Bir model olası hedeflerin bir listesini sunacaktır; Bildirim Hizmeti Uzantısı'nı seçmek için aşağı kaydırın veya filtreyi kullanın. Sonrakine tıkla .
  3. Bir ürün adı ekleyin (bu eğitimi takip etmek için "ImageNotification"ı kullanın), dili Objective-C olarak ayarlayın ve Son'a tıklayın.
  4. Etkinleştir'i tıklatarak şemayı etkinleştirin.

Adım 2 - Pod dosyasına hedef ekleyin

Yeni uzantınızın Firebase/Messaging bölmesine erişimi olduğundan emin olmak için onu Podfile'a ekleyin:

  1. Gezgin'den Pod dosyasını açın: Pod'lar > Pod dosyası

  2. Dosyanın en altına gidin ve şunu ekleyin:

    target 'ImageNotification' do
      use_frameworks!
      pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication
      pod 'Firebase/Messaging'
    end
    
  3. Pod'larınızı ios veya macos dizininden pod install kullanarak kurun veya güncelleyin.

Adım 3 - Uzantı yardımcısını kullanın

Bu noktada her şeyin hala normal şekilde çalışıyor olması gerekir. Son adım, uzantı yardımcısını çağırmaktır.

  1. Gezginden ImageNotification uzantınızı seçin

  2. NotificationService.m dosyasını açın.

  3. Dosyanın en üstünde, FirebaseMessaging.h aşağıda gösterildiği gibi NotificationService.h hemen ardından içe aktarın.

    NotificationService.m içeriğini şununla değiştirin:

    #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
    

Adım 4 - Görüntüyü veriye ekleyin

Artık bildirim verinize bir resim ekleyebilirsiniz. Gönderme isteğinin nasıl oluşturulacağını öğrenmek için iOS belgelerine bakın. Cihaz tarafından maksimum 300 KB görüntü boyutunun zorunlu tutulduğunu unutmayın.