You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 05:07:45