Firebase is back at Google I/O on May 10! Register now

รับข้อความในแอป Flutter

จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ

ข้อความขาเข้าจะได้รับการจัดการแตกต่างกันไปขึ้นอยู่กับสถานะของอุปกรณ์ เพื่อให้เข้าใจสถานการณ์เหล่านี้และวิธีรวม FCM เข้ากับแอปพลิเคชันของคุณเอง สิ่งสำคัญอันดับแรกคือต้องกำหนดสถานะต่างๆ ที่อุปกรณ์สามารถเป็นได้:

สถานะ คำอธิบาย
เบื้องหน้า เมื่อแอปพลิเคชันเปิดอยู่ในมุมมองและใช้งานอยู่
พื้นหลัง เมื่อแอปพลิเคชันเปิดอยู่ แต่อยู่ในพื้นหลัง (ย่อเล็กสุด) กรณีนี้มักเกิดขึ้นเมื่อผู้ใช้กดปุ่ม "หน้าแรก" บนอุปกรณ์ เปลี่ยนไปใช้แอปอื่นโดยใช้ตัวสลับแอป หรือเปิดแอปพลิเคชันในแท็บอื่น (เว็บ)
สิ้นสุด เมื่ออุปกรณ์ถูกล็อกหรือแอปพลิเคชันไม่ทำงาน

มีเงื่อนไขเบื้องต้นบางประการที่ต้องปฏิบัติตามก่อนที่แอปพลิเคชันจะสามารถรับเพย์โหลดข้อความผ่าน FCM:

  • ต้องเปิดแอปพลิเคชันอย่างน้อยหนึ่งครั้ง (เพื่อให้สามารถลงทะเบียนกับ FCM)
  • บน iOS หากผู้ใช้ปัดแอปพลิเคชันออกจากตัวสลับแอป จะต้องเปิดแอปนั้นใหม่ด้วยตนเองเพื่อให้ข้อความพื้นหลังเริ่มทำงานอีกครั้ง
  • ใน Android หากผู้ใช้บังคับปิดแอปจากการตั้งค่าอุปกรณ์ จะต้องเปิดแอปอีกครั้งด้วยตนเองเพื่อให้ข้อความเริ่มทำงาน
  • บนเว็บ คุณต้องขอโทเค็น (โดยใช้ getToken() ) กับใบรับรองการพุชเว็บของคุณ

ขออนุญาตรับข้อความ (Apple และ Web)

บน iOS, macOS และเว็บ ก่อนที่จะสามารถรับเพย์โหลด FCM บนอุปกรณ์ของคุณได้ คุณต้องขออนุญาตจากผู้ใช้ก่อน

แพ็คเกจ firebase_messaging มี API อย่างง่ายสำหรับการขออนุญาตผ่านเมธอด requestPermission API นี้ยอมรับอาร์กิวเมนต์ที่มีชื่อจำนวนหนึ่งซึ่งกำหนดประเภทของการอนุญาตที่คุณต้องการขอ เช่น การส่งข้อความที่มีเพย์โหลดการแจ้งเตือนสามารถเรียกเสียงหรืออ่านข้อความผ่าน Siri ได้หรือไม่ โดยค่าเริ่มต้น เมธอดจะร้องขอสิทธิ์เริ่มต้นที่เหมาะสม 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 ซึ่งให้รายละเอียดข้อมูลต่างๆ เกี่ยวกับเพย์โหลด เช่น แหล่งที่มา ID เฉพาะ เวลาที่ส่ง มีการแจ้งเตือนหรือไม่ และอื่นๆ เนื่องจากมีการดึงข้อความในขณะที่แอปพลิเคชันของคุณอยู่เบื้องหน้า คุณจึงสามารถเข้าถึงสถานะและบริบทของแอปพลิเคชัน Flutter ได้โดยตรง

ข้อความเบื้องหน้าและการแจ้งเตือน

ข้อความแจ้งเตือนที่มาถึงในขณะที่แอปพลิเคชันอยู่เบื้องหน้าจะไม่แสดงการแจ้งเตือนที่มองเห็นได้ตามค่าเริ่มต้น ทั้งบน Android และ iOS อย่างไรก็ตาม เป็นไปได้ที่จะแทนที่พฤติกรรมนี้:

  • บน Android คุณต้องสร้างช่องทางการแจ้งเตือน "ลำดับความสำคัญสูง"
  • บน iOS คุณสามารถอัปเดตตัวเลือกการนำเสนอสำหรับแอปพลิเคชันได้

ข้อความพื้นหลัง

กระบวนการจัดการข้อความพื้นหลังจะแตกต่างกันในแพลตฟอร์มเนทีฟ (Android และ Apple) และบนเว็บ

แพลตฟอร์ม Apple และ Android

จัดการข้อความพื้นหลังโดยลงทะเบียนตัวจัดการ onBackgroundMessage เมื่อได้รับข้อความ การแยกจะถูกสร้างขึ้น (เฉพาะ Android, iOS/macOS ไม่ต้องการการแยกแยกต่างหาก) ช่วยให้คุณจัดการข้อความได้แม้ในขณะที่แอปพลิเคชันของคุณไม่ได้ทำงานอยู่

มีบางสิ่งที่ควรทราบเกี่ยวกับตัวจัดการข้อความพื้นหลังของคุณ:

  1. จะต้องไม่เป็นฟังก์ชันนิรนาม
  2. ต้องเป็นฟังก์ชันระดับบนสุด (เช่น ไม่ใช่เมธอดคลาสที่ต้องเตรียมใช้งาน)
  3. ต้องมีคำอธิบายประกอบด้วย @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());
}

เนื่องจากตัวจัดการทำงานในแยกของตัวเองนอกบริบทแอปพลิเคชันของคุณ จึงไม่สามารถอัปเดตสถานะแอปพลิเคชันหรือดำเนินการ UI ที่ส่งผลกระทบต่อลอจิกได้ อย่างไรก็ตาม คุณสามารถดำเนินการตรรกะ เช่น คำขอ HTTP, ดำเนินการ IO (เช่น อัปเดตที่เก็บข้อมูลในเครื่อง), สื่อสารกับปลั๊กอินอื่น ๆ เป็นต้น

ขอแนะนำให้กรอกตรรกะของคุณโดยเร็วที่สุด การทำงานที่ยาวนานและเข้มข้นส่งผลกระทบต่อประสิทธิภาพของอุปกรณ์และอาจทำให้ระบบปฏิบัติการยุติกระบวนการ หากทำงานนานกว่า 30 วินาที อุปกรณ์อาจหยุดกระบวนการโดยอัตโนมัติ

เว็บ

บนเว็บ ให้เขียน JavaScript 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 ของคุณ ผู้ปฏิบัติงานจะได้รับการลงทะเบียนและข้อความเบื้องหลังจะถูกจัดการผ่านไฟล์นี้

ปฏิสัมพันธ์การจัดการ

เนื่องจากการแจ้งเตือนเป็นสิ่งที่มองเห็นได้ จึงเป็นเรื่องปกติที่ผู้ใช้จะโต้ตอบกับพวกเขา (โดยการกด) ลักษณะการทำงานเริ่มต้นทั้งบน Android และ 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. ใน payload ของเซิร์ฟเวอร์ แทนที่จะใช้ 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 หรือตัวเขียนการแจ้งเตือนหรือไม่

หากต้องการเปิดใช้งานการส่งออก ก่อนอื่นให้ทำตามขั้นตอน ที่อธิบายไว้ที่นี่ จากนั้นทำตามคำแนะนำเหล่านี้:

แอนดรอยด์

คุณสามารถใช้โค้ดต่อไปนี้: dart 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

เว็บ

สำหรับเว็บ คุณต้องเปลี่ยนพนักงานบริการของคุณเพื่อใช้ SDK เวอร์ชัน v9 ต้องรวมเวอร์ชัน v9 ดังนั้นคุณต้องใช้ตัวรวมเช่น esbuild เพื่อให้เจ้าหน้าที่บริการทำงาน ดู แอปตัวอย่าง เพื่อดูวิธีการบรรลุเป้าหมายนี้

เมื่อคุณย้ายไปที่ v9 SDK แล้ว คุณสามารถใช้รหัสต่อไปนี้:

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

...

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

อย่าลืมรัน yarn build เพื่อส่งออก Service Worker เวอร์ชันใหม่ไปยังโฟลเดอร์ web