Flutter uygulamasında mesaj alma

Bir 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 içinde olabileceği çeşitli durumları belirlemek önemlidir:

Durum Tanım
ön plan Uygulama açık, görünümde ve kullanımda olduğunda.
Arka plan Uygulama açıkken, ancak arka planda (küçültülmüş) olduğunda. 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 yoluyla mesaj yüklerini alabilmesi için karşılanması gereken birkaç ön koşul vardır:

  • Uygulamanın en az bir kez açılmış olması gerekir (FCM'ye kaydolmaya izin vermek için).
  • iOS'ta, kullanıcı uygulamayı uygulama değiştiriciden hızlıca 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ı, uygulamadan cihaz ayarlarından zorla çıkarsa mesajların çalışmaya başlaması için uygulamanın manuel olarak yeniden açılması gerekir.
  • Web'de, web push sertifikanızla ( getToken() kullanarak) bir belirteç istemiş olmanız gerekir.

Mesaj almak için izin isteme (Apple ve Web)

iOS, macOS ve web'de, FCM yüklerinin cihazınıza alınabilmesi için öncelikle kullanıcıdan izin almanız gerekir.

firebase_messaging paketi, requestPermission yöntemi aracılığıyla izin istemek için basit bir API sağlar. Bu API, bildirim yükleri içeren mesajlaşmanın bir sesi tetikleyip tetikleyemeyeceği veya Siri aracılığıyla mesajları okuyup okuyamayacağı gibi, talep etmek istediğiniz izin türlerini tanımlayan bir dizi adlandırılmış bağımsız değişkeni kabul eder. Varsayılan olarak, yöntem mantıklı varsayılan izinler ister. Referans API, her bir iznin ne için olduğuna dair eksiksiz belgeler sağlar.

Başlamak için, uygulamanızdan yöntemi çağırın (iOS'ta yerel bir modal 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ı izin verip vermemeyi henüz seçmedi.
  • provisional : Kullanıcı geçici izin verdi

NotificationSettings diğer özellikler, geçerli cihazda belirli bir iznin 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 yüklerini işlemeye başlayabilir.

Mesaj işleme

Uygulamanızın mevcut durumuna bağlı olarak, farklı mesaj türlerinin gelen yüklerini işlemek için farklı uygulamalar gerekir:

ön plan mesajları

Uygulamanız ön plandayken mesajları işlemek 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 kimliği, gönderme zamanı, bir bildirim içerip içermediği ve daha fazlası gibi çeşitli bilgileri detaylandı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şturmalısınız.
  • 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şleyicisi kaydederek arka plan mesajlarını işleyin. Mesajlar alındığında, uygulamanız çalışmıyorken bile mesajları işlemenize olanak tanıyan bir izolasyon oluşturulur (yalnızca Android, iOS/macOS ayrı bir izolasyon gerektirmez).

Arka plan ileti işleyiciniz hakkında akılda tutulması gereken birkaç şey vardır:

  1. Anonim bir işlev olmamalıdır.
  2. Üst düzey bir işlev olmalıdır (örneğin, başlatma gerektiren bir sınıf yöntemi değil).
  3. Flutter sürüm 3.3.0 veya üzerini kullanırken, mesaj işleyiciye işlev bildiriminin hemen üzerinde @pragma('vm:entry-point') ile açıklama 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 UI'yi etkileyen herhangi bir mantığı yürütmek mümkün değildir. Bununla birlikte, HTTP istekleri gibi mantık gerçekleştirebilir, IO işlemleri 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örevler yürütmek cihaz performansını etkiler ve işletim sisteminin işlemi 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 servis ç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, çalışan kayıtlı olmalıdır. 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 herhangi bir arka plan mesajı bu dosya aracılığıyla işlenecektir.

İşleme Etkileşimi

Bildirimler görünür bir ipucu olduğundan, kullanıcıların onlarla etkileşim kurması (basarak) yaygındır. Hem Android hem de iOS'ta varsayılan davranış, uygulamayı açmaktır. Uygulama sonlandırılırsa başlatılacaktır; arka planda ise ön plana getirilir.

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ı buna basarsa, uygulama açıldığında belirli bir sohbeti açmak isteyebilirsiniz.

firebase-messaging paketi, bu etkileşimi işlemek için iki yol sağlar:

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

Kullanıcılarınız için sorunsuz bir kullanıcı deneyimi sağlamak için 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 aldığınız, uygulama kurulumunuza bağlıdır. Yukarıdaki örnek, bir StatefulWidget kullanan temel bir çizimi göstermektedir.

Mesajları Yerelleştir

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 içinde Fransızca iletileri belirtin:

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

    Mesaj yükü şöyle görünür:

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

iOS

  1. Varsayılan dil mesajlarınızı Base.lproj/Localizable.strings içinde belirtin:

    "NOTIFICATION_TITLE" = "Hello World";
    "NOTIFICATION_MESSAGE" = "This is a message";
    
  2. Çevrilen iletileri language .lproj dizininde belirtin. Örneğin, fr.lproj/Localizable.strings içinde Fransızca mesajları belirtin:

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

    Mesaj yükü şöyle görünür:

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

Mesaj teslimi verilerini dışa aktarmayı etkinleştir

Daha fazla 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ünden veya mesajın API veya Notifications oluşturucu aracılığıyla gönderilip gönderilmediğinden bağımsız olarak, mesajlar için mevcut tüm verileri içerir.

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

Android

Aşağıdaki kodu kullanabilirsiniz:

await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

iOS için AppDelegate.m dosyasını aşağıdaki içerikle değiştirmeniz gerekmektedir.

#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ü kullanmak üzere hizmet çalışanınızı değiştirmeniz gerekir. v9 sürümünün paketlenmesi gerekir, bu nedenle hizmet çalışanının çalışmasını sağlamak için örneğ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çtikten sonra aşağıdaki kodu kullanabilirsiniz:

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

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

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

Resimleri iOS'ta bildirimlerde göster

Apple aygıtlarında, gelen FCM Bildirimlerinin FCM yükünden görüntüleri 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 Kimlik Doğrulama bölmesini Pod dosyanıza eklemelisiniz.

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

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

Adım 2 - Pod dosyasına hedef ekleyin

Yeni uzantınızı Pod dosyasına ekleyerek Firebase/Messaging bölmesine erişebildiğinden emin olun:

  1. Navigator'dan Podfile'ı açın: Pods > Podfile

  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. ios veya macos dizininden pod install kullanarak bölmelerinizi kurun veya güncelleyin.

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

Bu noktada, her şey hala normal çalışıyor olmalıdır. 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, aşağıda gösterildiği gibi NotificationService.h hemen sonra FirebaseMessaging.h 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ü yüke ekleyin

Bildirim yükünüze artık bir resim ekleyebilirsiniz. Bir gönderme isteğinin nasıl oluşturulacağına ilişkin iOS belgelerine bakın. Cihaz tarafından maksimum 300 KB görüntü boyutunun zorunlu kılındığını unutmayın.