收到!你遇到了一個常見的 Flutter 推播通知問題:在 Android 裝置上收到訊息,但沒有顯示通知。這通常是因為訊息在應用程式前景執行時,預設不會顯示視覺化的通知。
以下是針對你提供資訊的分析和解決方案:
💡 最簡化說明 (Simplest Explain/Summary)
當你的 Flutter App 在前景執行時收到 Firebase 推播通知,它會被你的 App 接收到(如日誌中的
D/FLTFireMsgReceiver
),但預設不會自動顯示為系統通知。你需要額外設定才能讓前景通知也顯示出來。📝 詳細說明 (Detailed Explain/Summary)
你提供的日誌
D/FLTFireMsgReceiver(31072): broadcast received for message
表示 Firebase 的接收器已經收到了推播訊息。然而,你沒有看到通知,這通常是因為以下原因:- 前景通知的預設行為 📱:
- 根據 Firebase Cloud Messaging (FCM) 的官方文件,當你的應用程式在前台運行時,接收到的通知訊息預設不會自動顯示為系統通知。這是為了避免在使用者正在使用 App 時,突然彈出一個干擾性的通知。
- 相反,這些訊息會透過
FirebaseMessaging.onMessage.listen()
這個監聽器傳遞給你的 Flutter 程式碼,讓你可以在 App 內自訂處理方式(例如顯示一個 App 內部的提示)。
- 需要手動處理前景通知 🛠️:
- 如果你希望在前台收到訊息時也顯示系統通知,你需要手動建立和顯示它。這通常涉及使用
flutter_local_notifications
套件,並在FirebaseMessaging.onMessage.listen()
的回調函數中觸發本地通知。
- 如果你希望在前台收到訊息時也顯示系統通知,你需要手動建立和顯示它。這通常涉及使用
- 其他可能原因 (較少見,但值得檢查):
- 權限問題 🔒: 確保你的 App 在 Android 裝置上已經獲得了接收通知的權限(尤其是 Android 13 及以上版本)。
- 通知通道 (Notification Channel) 📢: 在 Android 上,通知需要一個通知通道才能正確顯示。如果你的 App 沒有正確設定或建立通知通道,可能會影響通知的顯示。
firebase_messaging
版本問題 ⬆️: 有些舊版本的firebase_messaging
套件可能存在 Bug,影響了通知的處理。檢查一下你使用的版本,並參考 GitHub 上的相關 Issues(你提供的連結中有提到版本8.0.0-dev.9
的問題)。getToken()
的影響 🔑: 如 GitHub Issue #6011 所述,有時候在收到訊息前先呼叫FirebaseMessaging.instance.getToken()
會意外地觸發onMessage
的監聽器。這可能是一個 Bug 或是一個繞過問題的技巧,但最好還是尋求更標準的解決方案。
🚀 解決方案建議 (Solutions)
以下是解決這個問題的步驟:
- 請求通知權限 🛡️:
- 在你的應用程式啟動時(例如
main()
函數或initState
),請求使用者允許接收通知。 - 使用
firebase_messaging
套件的requestPermission()
方法:
dartimport 'package:firebase_messaging/firebase_messaging.dart'; Future<void> requestNotificationPermissions() async { 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}'); } // 在你的 main() 或 initState() 中呼叫 // await requestNotificationPermissions();
- 在你的應用程式啟動時(例如
- 處理前景訊息並顯示本地通知 🌟:
- 你需要整合
flutter_local_notifications
套件來顯示通知。 - 在
FirebaseMessaging.onMessage.listen()
的回調中,使用flutter_local_notifications
來建立和顯示一個本地通知。
步驟 2.1: 添加flutter_local_notifications
套件 在你的pubspec.yaml
中添加:yamldependencies: flutter: sdk: flutter firebase_core: ^x.x.x # 請使用你專案的 firebase_core 版本 firebase_messaging: ^x.x.x # 請使用你專案的 firebase_messaging 版本 flutter_local_notifications: ^x.x.x # 添加此套件
然後執行flutter pub get
。步驟 2.2: 初始化flutter_local_notifications
並在onMessage
中使用 在你的main.dart
或一個專門的服務類別中:dartimport 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; // 在 Android 上,需要設定通知通道 Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); print("Handling a background message: ${message.messageId}"); // 在背景處理訊息時,如果需要顯示通知,也需要使用 flutter_local_notifications // 但通常背景訊息是處理資料,而非顯示通知 } void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 初始化 Firebase // 初始化 flutter_local_notifications const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); // 你的 Android app icon const InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid); await flutterLocalNotificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) async { // 處理使用者點擊通知的邏輯 print('Notification tapped: ${notificationResponse.payload}'); }, ); // 設定前景訊息監聽器 FirebaseMessaging.onMessage.listen((RemoteMessage message) { print('Got a message whilst in the foreground!'); print('Message data: ${message.data}'); RemoteNotification? notification = message.notification; if (notification != null) { // 在前景時顯示本地通知 flutterLocalNotificationsPlugin.show( notification.hashCode, notification.title, notification.body, const NotificationDetails( android: AndroidNotificationDetails( 'high_priority_channel_id', // 這必須與 AndroidManifest.xml 中的 channel ID 一致 'High Priority Notifications', channelDescription: 'This channel is used for important notifications.', importance: Importance.high, priority: Priority.high, showWhen: false, // 你可以在這裡設定 icon, sound 等 // icon: '@mipmap/ic_launcher', ), ), payload: message.data.toString(), // 可以傳遞額外的資料 ); } }); // 設定背景訊息處理器 FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); runApp(const MyApp()); } // 全域變數,用於初始化 flutter_local_notifications final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override void initState() { super.initState(); requestNotificationPermissions(); // 在 initState 中請求權限 // 也可以在這裡訂閱 topic 或獲取 token FirebaseMessaging.instance.getToken().then((String? token) { print("FCM Token: $token"); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Firebase Messaging Demo'), ), body: const Center( child: Text('App is running'), ), ); } }
- 你需要整合
- 設定 Android Manifest 📝:
- 確保你在
android/app/src/main/AndroidManifest.xml
中有正確的設定,特別是關於通知通道的宣告。 - 如果你在上面的 Dart 代碼中使用了
high_priority_channel_id
,請確保你的 Manifest 中有對應的通道宣告(或者讓flutter_local_notifications
在第一次顯示時自動建立)。
範例AndroidManifest.xml
區塊:xml<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.your_app_name"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <!-- For Android 13+ --> <application android:label="your_app_name" android:icon="@mipmap/ic_launcher"> <activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <!-- Specifies an Android theme to apply to this Activity as soon as the Android process has started. This theme is visible to the user while the Flutter UI initializes. After that, this theme continues to determine the Window background behind the Flutter UI. --> <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="high_priority_channel_id"/> <!-- 預設通知通道 --> <!-- Specifies an Android theme to apply to this Activity as soon as the Android process has started. This theme is visible to the user while the Flutter UI initializes. After that, this theme continues to determine the Window background behind the Flutter UI. --> <meta-data android:name="flutterEmbedding" android:value="2" /> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- Don't delete the meta-data below. This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> <meta-data android:name="flutterEmbedding" android:value="2" /> <!-- Add this service for Firebase Messaging --> <service android:name="com.google.firebase.messaging.FirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> </application> </manifest>
- 重要: 如果你目標 Android 13 (API 33) 或以上,請務必添加
POST_NOTIFICATIONS
權限。
- 確保你在
- 檢查
firebase_messaging
版本和更新 🔄:- 如果你使用的是較舊的版本,例如
8.0.0-dev.9
,建議升級到最新的穩定版本。 - 執行
flutter pub outdated
查看是否有過期的套件,然後更新firebase_messaging
和firebase_core
。
- 如果你使用的是較舊的版本,例如
總結來說,最常見的解決方案是 在
onMessage
回調中加入 flutter_local_notifications
來手動顯示通知,並確保你已經請求了必要的權限。 👍
沒有留言:
發佈留言