קבל הודעות באפליקציית Flutter

בהתאם למצב המכשיר, הודעות נכנסות מטופלות בצורה שונה. כדי להבין את התרחישים הללו וכיצד לשלב את FCM באפליקציה שלך, תחילה חשוב לקבוע את המצבים השונים שבהם מכשיר יכול להיות:

מדינה תיאור
חֲזִית כאשר האפליקציה פתוחה, בתצוגה ובשימוש.
רקע כללי כאשר האפליקציה פתוחה, אך ברקע (ממוזער). זה מתרחש בדרך כלל כאשר המשתמש לחץ על כפתור "הבית" במכשיר, עבר לאפליקציה אחרת באמצעות מחליף האפליקציות, או שהאפליקציה פתוחה בכרטיסייה אחרת (אינטרנט).
הסתיים כאשר המכשיר נעול או שהאפליקציה לא פועלת.

ישנם מספר תנאים מוקדמים שחייבים לעמוד בהם לפני שהאפליקציה תוכל לקבל מטענים של הודעות באמצעות FCM:

  • האפליקציה חייבת להיפתח לפחות פעם אחת (כדי לאפשר רישום ב-FCM).
  • ב-iOS, אם המשתמש מחליק את האפליקציה ממחליף האפליקציות, יש לפתוח אותה מחדש באופן ידני כדי שהודעות רקע יתחילו לפעול שוב.
  • באנדרואיד, אם המשתמש עוזב את האפליקציה בכוח מהגדרות המכשיר, יש לפתוח אותה מחדש באופן ידני כדי שההודעות יתחילו לעבוד.
  • באינטרנט, עליך לבקש אסימון (באמצעות getToken() ) עם אישור ה-Push האינטרנט שלך.

בקש רשות לקבל הודעות

ב-iOS, macOS, אינטרנט ו-Android 13 (או חדש יותר), לפני שניתן יהיה לקבל עומסי FCM במכשיר שלך, תחילה עליך לבקש רשות מהמשתמש.

חבילת firebase_messaging מספקת API פשוט לבקשת הרשאה באמצעות שיטת requestPermission . ממשק API זה מקבל מספר ארגומנטים בעלי שם שמגדירים את סוג ההרשאות שברצונך לבקש, כגון האם הודעות המכילות עומסי הודעות יכולות להפעיל צליל או לקרוא הודעות דרך Siri. כברירת מחדל, השיטה מבקשת הרשאות ברירת מחדל הגיוניות. ה-Reference API מספק תיעוד מלא על מה מיועדת כל הרשאה.

כדי להתחיל, קרא לשיטה מהאפליקציה שלך (ב-iOS יוצג מודאל מקורי, באינטרנט תופעל זרימת ה-API המקורית של הדפדפן):

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

ניתן להשתמש במאפיין authorizationStatus של אובייקט NotificationSettings שהוחזר מהבקשה כדי לקבוע את ההחלטה הכוללת של המשתמש:

  • authorized : המשתמש העניק הרשאה.
  • denied : המשתמש דחה את ההרשאה.
  • notDetermined : המשתמש עדיין לא בחר אם להעניק הרשאה.
  • provisional : המשתמש העניק הרשאה זמנית

המאפיינים האחרים ב- NotificationSettings מחזירים אם הרשאה מסוימת מופעלת, מושבתת או לא נתמכת במכשיר הנוכחי.

לאחר שניתנה ההרשאה והסוגים השונים של מצב המכשיר הובנו, היישום שלך יכול כעת להתחיל לטפל במטעני FCM הנכנסים.

טיפול בהודעות

בהתבסס על המצב הנוכחי של האפליקציה שלך, עומסים נכנסים מסוגים שונים של הודעות דורשים יישומים שונים כדי לטפל בהם:

הודעות חזית

כדי לטפל בהודעות בזמן שהאפליקציה שלך נמצאת בחזית, האזן לזרם 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}');
  }
});

הזרם מכיל RemoteMessage , המפרט מידע שונה על המטען, כמו מאיפה הוא הגיע, המזהה הייחודי, זמן הנשלחה, האם הוא מכיל הודעה ועוד. מכיוון שההודעה אוחזרה בזמן שהאפליקציה שלך נמצאת בחזית, אתה יכול לגשת ישירות למצב ולהקשר של אפליקציית Flutter שלך.

הודעות חזית והודעות

הודעות התראה שמגיעות בזמן שהאפליקציה נמצאת בחזית לא יציגו התראה גלויה כברירת מחדל, הן באנדרואיד והן ב-iOS. עם זאת, ניתן לעקוף התנהגות זו:

  • באנדרואיד, עליך ליצור ערוץ הודעות "בעדיפות גבוהה".
  • ב-iOS, אתה יכול לעדכן את אפשרויות המצגת עבור האפליקציה.

הודעות רקע

תהליך הטיפול בהודעות רקע שונה בפלטפורמות מקוריות (אנדרואיד ואפל) ובפלטפורמות מבוססות אינטרנט.

פלטפורמות אפל ואנדרואיד

טפל בהודעות רקע על ידי רישום של מטפל onBackgroundMessage . כאשר הודעות מתקבלות, נוצר איזול (אנדרואיד בלבד, iOS/macOS אינו דורש איזול נפרד) המאפשר לך לטפל בהודעות גם כשהאפליקציה שלך לא פועלת.

יש כמה דברים שכדאי לזכור לגבי המטפל בהודעות הרקע שלך:

  1. אסור שזו תהיה פונקציה אנונימית.
  2. זה חייב להיות פונקציה ברמה העליונה (למשל לא שיטת מחלקה הדורשת אתחול).
  3. בעת שימוש ב-Flutter גרסה 3.3.0 ומעלה, יש לציין את מטפל ההודעות ב- @pragma('vm:entry-point') ממש מעל הצהרת הפונקציה (אחרת היא עשויה להיות מוסרת במהלך ניעור עצים למצב שחרור).
@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());
}

מכיוון שהמטפל פועל בבידוד משלו מחוץ להקשר של היישומים שלך, לא ניתן לעדכן את מצב היישום או לבצע לוגיקה משפיעה על ממשק המשתמש. עם זאת, אתה יכול לבצע לוגיקה כמו בקשות HTTP, לבצע פעולות IO (למשל עדכון אחסון מקומי), לתקשר עם תוספים אחרים וכו'.

כמו כן, מומלץ להשלים את ההיגיון בהקדם האפשרי. הפעלת משימות ארוכות ואינטנסיביות משפיעה על ביצועי המכשיר ועלולה לגרום למערכת ההפעלה להפסיק את התהליך. אם משימות פועלות יותר מ-30 שניות, המכשיר עשוי להרוג את התהליך באופן אוטומטי.

אינטרנט

באינטרנט, כתוב JavaScript Service Worker שפועל ברקע. השתמש ב-Service Worker כדי לטפל בהודעות רקע.

כדי להתחיל, צור קובץ חדש בספריית web שלך וקרא לו 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);
});

על הקובץ לייבא הן את האפליקציה והן את ערכות ה-SDK להעברת הודעות, לאתחל את Firebase ולחשוף את משתנה messaging .

לאחר מכן, העובד חייב להיות רשום. בתוך קובץ הכניסה, לאחר טעינת הקובץ main.dart.js , רשום את העובד שלך:

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

לאחר מכן הפעל מחדש את אפליקציית Flutter שלך. העובד יירשם וכל הודעות רקע יטופלו באמצעות קובץ זה.

טיפול באינטראקציה

מכיוון שהתראות הן רמז גלוי, נפוץ שמשתמשים מתקשרים איתן (על ידי לחיצה). התנהגות ברירת המחדל הן באנדרואיד והן ב-iOS היא פתיחת האפליקציה. אם הבקשה תסתיים היא תופעל; אם הוא ברקע הוא יובא לקדמת הבמה.

בהתאם לתוכן של הודעה, ייתכן שתרצה לטפל באינטראקציה של המשתמש בעת פתיחת האפליקציה. לדוגמה, אם הודעת צ'אט חדשה נשלחת באמצעות התראה והמשתמש לוחץ עליה, ייתכן שתרצה לפתוח את השיחה הספציפית עם פתיחת האפליקציה.

חבילת firebase-messaging מספקת שתי דרכים לטפל באינטראקציה זו:

  • getInitialMessage() : אם האפליקציה נפתחת ממצב סיום יוחזר Future המכיל RemoteMessage . לאחר צריכתו, ה- RemoteMessage יוסר.
  • onMessageOpenedApp : Stream שמפרסם RemoteMessage כאשר האפליקציה נפתחת ממצב רקע.

מומלץ לטפל בשני התרחישים כדי להבטיח UX חלק עבור המשתמשים שלך. דוגמת הקוד שלהלן מתארת ​​כיצד ניתן להשיג זאת:

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

האופן שבו אתה מטפל באינטראקציה תלוי בהגדרת היישום שלך. הדוגמה שלמעלה מציגה איור בסיסי באמצעות StatefulWidget.

התאמה של הודעות

אתה יכול לשלוח מחרוזות מקומיות בשתי דרכים שונות:

  • אחסן את השפה המועדפת של כל אחד מהמשתמשים שלך בשרת שלך ושלח הודעות מותאמות אישית לכל שפה
  • הטמע מחרוזות מקומיות באפליקציה שלך ועשה שימוש בהגדרות המקומיות המקוריות של מערכת ההפעלה

הנה איך להשתמש בשיטה השנייה:

דְמוּי אָדָם

  1. ציין את הודעות ברירת המחדל שלך ב- resources/values/strings.xml :

    <string name="notification_title">Hello world</string>
    <string name="notification_message">This is a message</string>
    
  2. ציין את ההודעות המתורגמות בספריית values- language . לדוגמה, ציין הודעות בצרפתית ב- resources/values-fr/strings.xml :

    <string name="notification_title">Bonjour le monde</string>
    <string name="notification_message">C'est un message</string>
    
  3. במטען השרת, במקום להשתמש במפתחות title , message body , השתמש title_loc_key וב- body_loc_key עבור ההודעה המותאמת לך, והגדר אותם לתכונת name של ההודעה שברצונך להציג.

    מטען ההודעה ייראה כך:

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

iOS

  1. ציין את הודעות ברירת המחדל שלך ב- Base.lproj/Localizable.strings :

    "NOTIFICATION_TITLE" = "Hello World";
    "NOTIFICATION_MESSAGE" = "This is a message";
    
  2. ציין את ההודעות המתורגמות בספריית language .lproj . לדוגמה, ציין הודעות בצרפתית ב- fr.lproj/Localizable.strings :

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

    מטען ההודעה ייראה כך:

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

אפשר ייצוא נתוני מסירת הודעות

אתה יכול לייצא את נתוני ההודעות שלך ל-BigQuery לניתוח נוסף. BigQuery מאפשרת לך לנתח את הנתונים באמצעות BigQuery SQL, לייצא אותם לספק ענן אחר, או להשתמש בנתונים עבור מודלים ML המותאמים אישית שלך. ייצוא ל-BigQuery כולל את כל הנתונים הזמינים עבור הודעות, ללא קשר לסוג ההודעה או אם ההודעה נשלחת דרך ה-API או מחבר ההודעות.

כדי להפעיל את הייצוא, בצע תחילה את השלבים המתוארים כאן , ולאחר מכן בצע את ההוראות הבאות:

דְמוּי אָדָם

אתה יכול להשתמש בקוד הבא:

await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

עבור iOS, עליך לשנות את AppDelegate.m עם התוכן הבא.

#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

אינטרנט

עבור אינטרנט, עליך לשנות את ה-Service Worker שלך כדי להשתמש בגרסת v9 של SDK. גרסת v9 צריכה להיות מאגדת, אז אתה צריך להשתמש באנדלר כמו esbuild למשל כדי לגרום ל-Service Worker לעבוד. עיין באפליקציה לדוגמה כדי לראות כיצד להשיג זאת.

לאחר המעבר ל-v9 SDK, תוכל להשתמש בקוד הבא:

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

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

אל תשכח להפעיל yarn build כדי לייצא את הגרסה החדשה של ה-Service Worker שלך לתיקיית web .

הצג תמונות בהתראות ב-iOS

במכשירי אפל, על מנת שהודעות FCM נכנסות יציגו תמונות ממטען ה-FCM, עליך להוסיף תוסף נוסף לשירות התראות ולהגדיר את האפליקציה שלך להשתמש בה.

אם אתה משתמש באימות טלפון של Firebase, עליך להוסיף את תרמיל Firebase Auth ל-Podfile שלך.

שלב 1 - הוסף תוסף לשירות התראות

  1. ב-Xcode, לחץ על קובץ > חדש > יעד...
  2. מודל יציג רשימה של יעדים אפשריים; גלול מטה או השתמש במסנן כדי לבחור בהרחבת שירות התראות . הקש "הבא .
  3. הוסף שם מוצר (השתמש ב-"ImageNotification" כדי לעקוב אחר המדריך הזה), הגדר את השפה ל-Objective-C ולחץ על סיום .
  4. הפעל את הסכימה על ידי לחיצה על הפעל .

שלב 2 - הוסף יעד ל-Podfile

ודא שלתוסף החדש שלך יש גישה לתרמיל Firebase/Messaging על ידי הוספתו ב-Podfile:

  1. מהנווט, פתח את ה-Podfile: Pods > Podfile

  2. גלול מטה לתחתית הקובץ והוסף:

    target 'ImageNotification' do
      use_frameworks!
      pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication
      pod 'Firebase/Messaging'
    end
    
  3. התקן או עדכן את הפודים שלך באמצעות pod install מספריית ios או macos .

שלב 3 - השתמש בעוזר ההרחבה

בשלב זה, הכל אמור עדיין לפעול כרגיל. השלב האחרון הוא הפעלת עוזר ההרחבה.

  1. מהנווט, בחר בתוסף ImageNotification שלך

  2. פתח את הקובץ NotificationService.m .

  3. בחלק העליון של הקובץ, ייבא את FirebaseMessaging.h מיד אחרי ה- NotificationService.h כפי שמוצג להלן.

    החלף את התוכן של NotificationService.m ב:

    #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
    

שלב 4 - הוסף את התמונה למטען

במטען ההתראות שלך, כעת תוכל להוסיף תמונה. עיין בתיעוד של iOS כיצד לבנות בקשת שליחת . זכור שגודל תמונה מקסימלי של 300KB נאכף על ידי המכשיר.