---
tags: Flutter
---
# Flutter 導入 Firebase Cloud Message (FCM)
以下會簡介Flutter導入FCM的做法:
1. 導入FCM SDK
2. 撰寫接收push message 程式碼
3. 測試data message 方法
## 導入FCM SDK
Flutter dependencies設定如下:
```yaml
dependencies:
flutter:
sdk: flutter
# 以下新增firebase message相關dependencies
firebase_core: ^1.19.1
firebase_messaging: ^11.4.2
```
### Android 相關設定
**Firebase console**
要使用Firebase的功能,需要現在Firebase上建立專案,並在專案中新增Android應用程式。並再根據表單中的需求填入資料。

註冊應用程式後,再將google-service.json檔下載到Flutter中的Android專案內。

或是你也可以在專案的一般設定的頁面中找到下載json檔的下載按鈕。

**android gradle**
請確認Flutter之下的Android專案中是否已具備以下設定。
在專案目錄下的gradle增加以下設定:
```groovy
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
//新增google-service
classpath 'com.google.gms:google-services:4.3.8'
}
```
在app層之下的gradle新增以下設定:
```groovy
apply plugin: 'com.google.gms.google-services'
```
### iOS 相關設定
因為iOS推播需要在Apple開發帳號中進行APNs相關設定,而我目前沒有Apple的開發帳號,故而如果需要進行iOS推播設定可以參考以下網址:
https://firebase.flutter.dev/docs/messaging/apple-integration
## push message 接收方法
在接收push message時可以分文以下三種狀態下接收。
1. 前景
2. 背景
3. APP關閉
而不同狀態下接受的push message,則是透過不同的founction來處理。
首先,在main之下新增firebase初始化程式。
```dart
void main() async {
//firebase初始化。
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
```
接著對firebase message元件進行初始化設定。
```dart
void _initFirebaseMessage()async{
FirebaseMessaging messaging = FirebaseMessaging.instance;
//for iOS 的權限設定
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true
);
if(settings.authorizationStatus != AuthorizationStatus.authorized){
//顯示提示Dialog
DialogUtil.showMessageDialog(context, "如果不授予權限,將無法收到推播訊息進行驗證。");
}else{
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
//APP關閉時,透過原生Notification點進APP時的觸發點
print('Got a message whilst in the getInitialMessage!');
StringBuffer stringBuffer = StringBuffer();
stringBuffer.write("Got a message whilst in the getInitialMessage! \n");
if(message != null){
print("data: ${message.data}");
message.data.forEach((key, value) {
stringBuffer.write("key: $key, value: $value \n");
});
setState((){
_push_messsage = stringBuffer.toString();
});
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message){
//前景接message
print('Got a message whilst in the onMessage!');
StringBuffer stringBuffer = StringBuffer();
stringBuffer.write("Got a message whilst in the onMessage! \n");
if(message != null){
print("data: ${message.data}");
message.data.forEach((key, value) {
stringBuffer.write("key: $key, value: $value \n");
});
setState((){
_push_messsage = stringBuffer.toString();
});
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message){
//APP在背景時,點Notification進入APP時的觸發點。
print('Got a message whilst in the onMessageOpenedApp!');
StringBuffer stringBuffer = StringBuffer();
stringBuffer.write("Got a message whilst in the onMessageOpenedApp! \n");
if(message != null){
print("data: ${message.data}");
message.data.forEach((key, value) {
stringBuffer.write("key: $key, value: $value \n");
});
setState((){
_push_messsage = stringBuffer.toString();
});
}
});
}
}
```
面下則是取得token的方法:
```dart=
void _getToken() async{
FirebaseMessaging messaging = FirebaseMessaging.instance;
late String? token;
token = await messaging.getToken();
if(token != null){
print("token: $token");
setState(() {
_push_token = token!;
});
}
}
```
## Data Message 測試方法。
我們都知道FCM可以分為兩種訊息:
1. Notification
2. Data Message
其中Data Message才可以夾帶payload,而想要對其進行測試的話,因為Firebase console只提供Notification訊息的推送測試,所以要測試的話只能依靠firebase提供的API進行推送。以下我會根據舊版的API透過postmae進行Data Message的推送觸發。
在專案設定的雲端通訊頁面中啟用舊版的API,啟用完成後可以拿到server key。

發送Data Message的URL如下
```
https://fcm.googleapis.com/fcm/send
```
Header 新增以下兩個參數:
1. Content-Type = application/json
2. Authorization = key=你專案的server key

request body 內容如下:(選擇raw以及JSON)

```json
{
"to" : "your token",
"notification" : {
"body" : "Body of Your Notification",
"title": "Title of Your Notification"
},
"data" : {
"body" : "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1" : "Value for key_1",
"key_2" : "Value for key_2"
}
}
```
發送成功後的response

```
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp().whenComplete((){
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(new MyApp());
// FirebaseMessagingManager.initFirebaseMessage();
encryptKeyAndIv();
});
});
}
```
```dart=
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
});
```
## 參考資料
https://firebase.flutter.dev/docs/messaging/overview
https://blog.kevinyang.net/2021/01/13/flutter-firebase-message-setup/
https://medium.com/android-school/test-fcm-notification-with-postman-f91ba08aacc3