การส่ง Push Notification Message ไปยัง Flutter App ด้วย Firebase Cloud Messaging (FCM) เป็นคำถามที่ถูกถามมาโดยตลอด และทุกครั้งที่ถูกถามก็จะหา link ต่างๆ ส่งไปให้ แต่ก็มักได้คำตอบมาว่าทำตามไม่ได้ ซึ่งก็ไม่แน่ใจว่าเพราะสาเหตุใดกันแน่ บทความนี้จึงสรุปเก็บไว้ให้ เพื่อแสดงให้เห็นว่าเราสามารถเริ่มต้นใช้งานมันได้อย่างไม่ยากเย็น
Setup โปรเจ็ค
ใช้แค่ 4 ขั้นตอนนี้เท่านั้นในการทำให้รับ Message ได้
1. สร้าง Project
- สร้าง Firebase Project แล้วไม่ต้องทำอะไรต่อให้จำชื่อโปรเจ็คไว้ให้ดี
- สร้าง Flutter Project ด้วยคำสั่ง
flutter create fcm_example
2. แก้ไข Application ID
- เปิดไฟล์
android/app/build.gradle
และกำหนดค่าapplicationId
เป็น id ของแอปที่เราต้องการ เช่นcom.khomkrit.app
- เปิดไฟล์
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 ฟังก์ชั่น คือ
onMessage
— สำหรับรอรับ Message ที่ได้รับตอนที่เปิดแอปใช้งานอยู่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 ส่วน คือ
- Configure จาก Xcode โดยเปิดไฟล์
ios/Runner.xcworkspace
ด้วย Xcode ขึ้นมาแก้ - 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 หากันแล้วรับได้เท่านั้น เวลาที่ต้องทำจริง ยังมีสิ่งอื่นที่ต้องทำเพิ่มอีก