Home Coding Flutter Push Notification Message / APNs และ Firebase Cloud Messaging (FCM)

Flutter Push Notification Message / APNs และ Firebase Cloud Messaging (FCM)

by khomkrit
5K views

การส่ง Push Notification Message ไปยัง Flutter App ด้วย Firebase Cloud Messaging (FCM) เป็นคำถามที่ถูกถามมาโดยตลอด และทุกครั้งที่ถูกถามก็จะหา link ต่างๆ ส่งไปให้ แต่ก็มักได้คำตอบมาว่าทำตามไม่ได้ ซึ่งก็ไม่แน่ใจว่าเพราะสาเหตุใดกันแน่ บทความนี้จึงสรุปเก็บไว้ให้ เพื่อแสดงให้เห็นว่าเราสามารถเริ่มต้นใช้งานมันได้อย่างไม่ยากเย็น

Setup โปรเจ็ค

ใช้แค่ 4 ขั้นตอนนี้เท่านั้นในการทำให้รับ Message ได้

1. สร้าง Project

  1. สร้าง Firebase Project แล้วไม่ต้องทำอะไรต่อให้จำชื่อโปรเจ็คไว้ให้ดี
  2. สร้าง Flutter Project ด้วยคำสั่ง
flutter create fcm_example

2. แก้ไข Application ID

  1. เปิดไฟล์ android/app/build.gradle และกำหนดค่า applicationId เป็น id ของแอปที่เราต้องการ เช่น com.khomkrit.app
  2. เปิดไฟล์ ios/Runnder.xcodeproj/project/pbxproj และไฟล์ macos/Runner/Configs/AppInfo.xcconfig และกำหนดค่า PRODUCT_BUNDLE_IDENTIFIER เป็น id ของแอปที่เราต้องการ เช่น com.khomkrit.app

3. Auto Configure Firebase

รันคำสั่งพวกนี้มันจะสร้าง firebase config ในโปรเจ็คของเรา พร้อมกับไปสร้าง project ใน firebase console ให้เลยอัตโนมัติ

dart pub global activate flutterfire_cli
flutterfire configure

แล้วทำตามขั้นตอนไปเรื่อยๆ จนจบ จากนั้นตรวจผลลัพธ์ที่ได้จากการรัน configure จาก 2 ที่ต่อไปนี้

Firebase Console — เข้าไปดูที่ Project Settings ของโปรเจ็คเราใน firebase console ที่แท็บ General และ Cloud Messaging จะต้องมีแอปเราถูกสร้างขึ้นมา และมี application ID ตรงกับที่เราต้องการ

Flutter Project — มีไฟล์ lib/firebase_options.dart และเช็คว่ามี bundle id ตรงกับที่เราต้องการ

4. ลองส่ง Push Notification ไปที่ Android Device

ติดตั้ง dependency ด้วยคำสั่งนี้

flutter pub add firebase_core
flutter pub add firebase_messaging

เพิ่มโค้ดนี้ลงไปในฟังก์ชั่น main() และอย่าลืม import dependency ที่เกี่ยวข้อง

WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
debugPrint('device token = ${await FirebaseMessaging.instance.getToken()}');

เมื่อรันโปรเจ็คไปที่ Android Device เราจะเห็น device token ออกมาทาง console ให้ copy ค่านี้ไว้

กลับไปที่หน้า Home เพื่อพับแอปเก็บไว้ก่อน

ลองส่ง Message ที่ Firebase Console โดยไปที่เมนู Engage → Cloud Messaging แล้วเลือก Send your first message (ณ ตอนที่เขียนบทความนี้ UI เป็นแบบนี้ ในอนาคตอาจต่างกันออกไป) และกรอก device token ลงไปเพื่อระบุเครื่องปลายทางที่จะรับ

เรารับ Push Notification Message ได้แล้วโดยที่แทบไม่ต้องเขียนโค้ดอะไรเลย

เขียนโค้ดรอรับ Push Notification Message

เราจะใช้ 2 ฟังก์ชั่น คือ

  1. onMessage — สำหรับรอรับ Message ที่ได้รับตอนที่เปิดแอปใช้งานอยู่
  2. onBackgroundMessage — สำหรับรอรับ Message ที่ได้รับตอนที่แอปรันอยู่ใน Background

เพิ่มฟังก์ชั่นนี้ลงไป เอาวางไว้ข้างนอกคลาส

Future<void> firebaseMessagingHandler(RemoteMessage message) async {
  debugPrint('Notification: ${message.notification?.title ?? ''}');
}

เพิ่มฟังก์ชั่นนี้ลงไป และเรียกใช้ในระหว่าง initState

Future<void> _initFirebase() async {
  FirebaseMessaging.onMessage.listen(firebaseMessagingHandler);
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingHandler);
}

จากนั้นลองส่ง Push Notification Message จาก Firebase Console ใหม่ จะพบว่า firebaseMessagingHandler ถูกเรียกทั้งตอนที่แอปอยู่ใน foreground และ background ตามต้องการ

Handle การเปิดแอปขึ้นมาจาก Push Notification

เพิ่มฟังก์ชั่นนี้ลงไป แล้วเรียกใช้หลังจาก _initFirebase()

void _setupInteractedMessage() async {
  // Open from a terminated state
  FirebaseMessaging messaging = FirebaseMessaging.instance;
  RemoteMessage? initialMessage = await messaging.getInitialMessage();
  setState(() => _message = initialMessage?.notification?.title ?? '');

  // Open app from the background state
  FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
    setState(() => _message = message.notification?.title ?? '');
  });
}

เขียนโค้ดแสดงค่าจากตัวแปร _message ที่หน้าจอ

จากนั้นรันแอปขึ้นมา แล้วปิดแอป ลองส่ง Notification Message และกดเปิดแอปเข้ามาจาก Notification จะเห็นค่าในตัวแปร _message เปลี่ยนไปตาม Notification Message ที่ได้รับ

ทำให้ใช้งานได้กับ Apple iOS / APNs

ทำตาม Guideline นี้ ซึ่งจะแบ่งออกเป็น 2 ส่วน คือ

  1. Configure จาก Xcode โดยเปิดไฟล์ ios/Runner.xcworkspace ด้วย Xcode ขึ้นมาแก้
  2. Link APNs กับ FCM (Firebase Cloud Messaging) — และไม่จำเป็นต้องทำส่วน Advanced ใน Guideline

โหลด GoogleService-Info.plist จาก Firebase Console: Project Settings → General → Apple apps แล้วนำไปใส่ไว้ใน Xcode Project ตามรูป

หลังจากทำตาม Guideline ดังกล่าวแล้ว ให้เปิดไฟล์ ios/Podfile และแก้ค่าของ platform เป็น :ios, '14.0' แล้วรันโปรเจ็คไปที่ iOS Device

เพิ่มฟังก์ชั่นนี้ลงไป แล้วเรียกใช้ในฟังก์ชั่น _initFirebase() ก่อน จากนั้นถึงจะ get device token

Future<bool> _requestPermission() async {
  FirebaseMessaging messaging = FirebaseMessaging.instance;
  NotificationSettings settings = await messaging.requestPermission(
    alert: true,
    badge: true,
    provisional: false,
    sound: true,
  );

  return (settings.authorizationStatus == AuthorizationStatus.authorized ||
      settings.authorizationStatus == AuthorizationStatus.provisional);
}

ดังนั้น ตอนนี้ฟังก์ชั่น _initFirebase() ก็จะเป็นแบบนี้แล้ว

Future<void> _initFirebase() async {
  bool allow = await _requestPermission();

  if (!allow) return;

  debugPrint('device token = ${await FirebaseMessaging.instance.getToken()}');

  FirebaseMessaging.onMessage.listen(_firebaseMessagingHandler);
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingHandler);
}

เพียงเท่านี้ iOS ก็สามารถรับ Push Message ได้แล้ว

ส่ง Push Notification Message ไป Firebase Cloud Messaging เองจาก Node.js

สร้าง และโหลด Private Key ที่ Firebase Console: Project settings → Service accounts

ติดตั้ง package firebase-admin และ init Firebase Admin SDK ดังนี้

var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});

และลองส่ง Mesasge เช่น

admin.messaging().sendMulticast({
  tokens: ["token", "token"],
  notification: {
    title: "Weather Warning!",
    body: "A new weather warning has been issued for your location.",
  },
  data: {'key':'value'}
})

ส่งแล้ว iOS ไม่ได้รับข้อความ?

  • ให้ลอง get APNs token มา ถ้าได้ค่าเป็น null แปลว่าการ config ทางฝั่ง Apple ไม่ถูกต้อง
await FirebaseMessaging.instance.getAPNSToken();
  • ลองทำตาม Guideline FCM via APNs Integration ใหม่อีกรอบ อย่างละเอียด อย่าข้าม
  • ลบแอปลงใหม่ และเช็คเรื่อง Permission จาก Settings ของ iOS เอง ว่าเคยไม่ให้หรือไม่?
  • เช็ค Bundle ID ให้ตรงกันหมด ทั้งจาก Firebase Console และจาก Apple Developer Portal / App Identity

ถ้าได้ APNs Token มาแล้ว แต่ก็ยังส่ง Message ผ่าน FCM ไม่ได้อีก?

ให้ลองเปลี่ยนมาใช้ APNs token ที่ได้ มาใช้ส่ง Message ผ่าน node-apn แทนที่จะใช้ Device Token ของ FCM ดู

เอาโค้ดนี้ไปรัน ถ้าส่งได้ แปลว่าอาจมีบางอย่างผิดปกติที่ FCM

var apn = require('node-apn');
var options = {
  token: {
    key: "/path/to/key.p8",
    keyId: "key-id",
    teamId: "team-id"
  },
  production: false
};
 
var apnProvider = new apn.Provider(options);

var note = new apn.Notification();
 
note.badge = 3;
note.alert = "\uD83D\uDCE7 \u2709 You have a new message";
note.payload = {'messageFrom': 'John Appleseed'};
note.topic = "my.app.id";

let deviceToken = "apns-token";
apnProvider.send(note, deviceToken).then( (result) => {
  console.log(result);
});

Dependency

  • firebase_core: ^1.10.6
  • firebase_messaging: ^11.2.4
  • firebase-admin”: “^10.0.1”
  • Flutter 2.8.0
  • Dart 2.15.0

โค้ดในบทความนี้เป็นแค่ตัวอย่างเพื่อช่วยให้สบายใจว่าสามารถส่ง Push Notification Message หากันแล้วรับได้เท่านั้น เวลาที่ต้องทำจริง ยังมีสิ่งอื่นที่ต้องทำเพิ่มอีก

Download Example Source Code

You may also like