Flutter FCM中从后台返回前台时onMessageOpenedApp回调的处理
Flutter FCM: 后台唤起时onMessageOpenedApp不触发的解决方案
核心原因
当你通过onBackgroundMessage手动创建本地通知时,FCM默认的通知分发逻辑会被绕过——onMessageOpenedApp只监听FCM系统推送的原生通知点击事件,手动创建的本地通知点击事件需要由本地通知插件自行处理。
解决步骤
1. 配置本地通知插件的点击回调
如果使用flutter_local_notifications创建自定义通知,必须在初始化时设置onSelectNotification回调,用来捕获本地通知的点击事件。
2. 传递FCM消息数据到本地通知
在onBackgroundMessage中创建本地通知时,要把FCM的RemoteMessage数据序列化为JSON字符串,作为本地通知的payload传入,这样点击时能还原原始消息数据。
3. 统一处理所有通知点击逻辑
把通知点击后的业务逻辑(比如页面跳转)抽成公共方法,同时在onMessageOpenedApp、本地通知回调、冷启动初始化消息中调用,确保所有场景下逻辑一致。
代码示例
1. 顶级后台消息处理器(必须是顶级函数)
import 'dart:convert'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); final FlutterLocalNotificationsPlugin localNotifications = FlutterLocalNotificationsPlugin(); // 配置Android通知渠道(必填) const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'your_custom_channel_id', '自定义通知渠道', channelDescription: '用于后台自定义通知展示', importance: Importance.max, priority: Priority.high, ); const NotificationDetails notificationDetails = NotificationDetails(android: androidDetails); // 发送本地通知,携带FCM消息的完整数据 await localNotifications.show( message.hashCode, message.notification?.title ?? '通知标题', message.notification?.body ?? '通知内容', notificationDetails, payload: jsonEncode(message.toMap()), ); }
2. 初始化本地通知并配置点击回调
final FlutterLocalNotificationsPlugin localNotifications = FlutterLocalNotificationsPlugin(); void initLocalNotifications() async { const AndroidInitializationSettings androidInit = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initSettings = InitializationSettings(android: androidInit); await localNotifications.initialize( initSettings, onSelectNotification: (String? payload) async { if (payload == null) return; // 还原FCM原始消息 final Map<String, dynamic> messageMap = jsonDecode(payload); final RemoteMessage message = RemoteMessage.fromMap(messageMap); // 调用公共处理逻辑 _handleNotificationTap(message); }, ); }
3. 统一通知点击处理逻辑
// 建议使用全局导航键确保后台唤起时上下文可用 final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); void _handleNotificationTap(RemoteMessage message) { // 这里写你的业务逻辑,比如根据消息数据跳转页面 if (message.data.containsKey('route')) { navigatorKey.currentState?.pushNamed( message.data['route'], arguments: message.data['params'], ); } }
4. 初始化FCM并监听所有场景
void initFCM() async { FirebaseMessaging messaging = FirebaseMessaging.instance; // 注册后台消息处理器 FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); // 监听前台消息(可选) FirebaseMessaging.onMessage.listen((RemoteMessage message) { _showForegroundNotification(message); }); // 监听FCM原生通知的点击(未自定义后台通知时触发) FirebaseMessaging.onMessageOpenedApp.listen(_handleNotificationTap); // 处理冷启动时的通知点击 RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { _handleNotificationTap(initialMessage); } } // 前台显示通知的辅助方法(可选) void _showForegroundNotification(RemoteMessage message) async { const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'foreground_channel_id', '前台通知渠道', importance: Importance.defaultImportance, ); const NotificationDetails notificationDetails = NotificationDetails(android: androidDetails); await localNotifications.show( message.hashCode, message.notification?.title, message.notification?.body, notificationDetails, payload: jsonEncode(message.toMap()), ); }
注意事项
- 确保
flutter_local_notifications与firebase_messaging版本兼容,避免依赖冲突 - Android端必须创建通知渠道,否则后台通知可能无法显示
- iOS端需要在
Info.plist中配置推送权限,开启远程通知能力 onBackgroundMessage是运行在隔离区的顶级函数,不能使用任何Flutter上下文相关的API(比如直接调用Navigator),只能通过本地通知插件传递数据
内容的提问来源于stack exchange,提问作者Yogesh Dubey
相关产品推荐
相关产品推荐

