Flutter Firebase推送getInitialMessage为null、点击回调不触发
问题根因
- 推送payload与代码逻辑不匹配:后端当前推送仅携带
notification字段,未携带自定义data字段,但你的代码中所有读取通知标题、内容的逻辑都从message.data取值,实际运行时message.data为空对象,不仅会导致手动弹出的通知标题内容为空,还会触发空指针报错打断消息处理流程。 - 通知点击事件归属逻辑混淆:Firebase Messaging提供的
getInitialMessage、onMessageOpenedApp两个回调,仅能响应FCM系统自动弹出的通知的点击事件。你在前台消息监听、后台消息处理回调中手动调用flutter_local_notifications弹出的通知属于本地通知,FCM不会接管这部分通知的点击事件,必须通过本地通知插件自身的回调监听点击。 - 本地通知配置缺失:你在多个位置重复初始化
FlutterLocalNotificationsPlugin,但全程没有配置本地通知的点击回调,也没有处理应用冷启动时点击本地通知的场景,手动弹出的通知点击后自然不会触发跳转。 - 代码逻辑存在硬伤:
getInitialMessage回调中直接使用message!做强制非空断言,当message为null时直接触发红屏报错;没有使用全局导航Key,跳转时依赖的State上下文在路由树未挂载完成时无效,导致跳转失败;后台消息处理中重复初始化本地通知、重复弹出通知,既会造成通知栏显示多条重复消息,也会打乱FCM的事件分发逻辑。 - 权限与清单配置缺失:安卓12及以上版本未给启动Activity配置
exported="true"属性、安卓13+未申请通知权限、iOS端关闭了通知权限申请,都会导致通知点击事件无法正常分发给应用。
修复方案
按步骤调整即可:
- 统一全局导航能力
在全局位置定义导航Key,避免路由跳转时拿不到有效上下文:
给MaterialApp绑定该Key:final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();MaterialApp( navigatorKey: navigatorKey, // 绑定全局key debugShowCheckedModeBanner: false, home: SplashScreen(), // 其他配置保持不变 ) - 统一本地通知初始化逻辑
删除_firebaseMessagingBackgroundHandler、initState中所有本地通知初始化的重复代码,在main函数中统一初始化本地通知,同时配置点击回调、冷启动点击处理逻辑:
在main函数Future<void> initLocalNotifications() async { const AndroidInitializationSettings androidInit = AndroidInitializationSettings('@mipmap/ic_launcher'); const IOSInitializationSettings iosInit = IOSInitializationSettings( requestSoundPermission: true, requestBadgePermission: true, requestAlertPermission: true, ); const InitializationSettings initSettings = InitializationSettings(android: androidInit, iOS: iosInit); // 注册前台运行时本地通知点击回调 await flutterLocalNotificationsPlugin.initialize( initSettings, onSelectNotification: (String? payload) async { if (payload == null) return; // 可根据payload携带的参数跳转不同页面,示例为跳转指定页面 navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => DoneAndPaiedPagess(0))); }, ); // 处理应用完全终止时,点击本地通知启动的场景 final NotificationAppLaunchDetails? launchDetail = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); if (launchDetail != null && launchDetail.didNotificationLaunchApp) { WidgetsBinding.instance.addPostFrameCallback((_) { navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => DoneAndPaiedPagess(0))); }); } }runApp之前调用await initLocalNotifications();完成初始化。 - 调整FCM消息处理逻辑,避免重复弹通知
- 清理
_firebaseMessagingBackgroundHandler逻辑:该回调中仅处理角标存储等数据逻辑,不要调用flutterLocalNotificationsPlugin.show弹通知——应用在后台、终止状态时,只要payload携带notification字段,FCM会自动弹出系统通知,手动重复弹会导致通知重复、点击事件无法被FCM捕获。 - 调整前台
onMessage逻辑:应用在前台时FCM不会自动弹通知,此时再调用本地通知插件弹出通知即可,弹通知时传入payload供点击回调使用:FirebaseMessaging.onMessage.listen((RemoteMessage message) { final RemoteNotification? notification = message.notification; final AndroidNotification? androidNoti = message.notification?.android; // 原有角标处理逻辑 if (message.data['title']?.toString().toLowerCase() == "new request") { SessionManager sessionManager = SessionManager(navigatorKey.currentContext!); sessionManager.getBadge().then((badge) { final int newBadge = (badge ?? 0) + 1; strikeNotifier.value = newBadge; sessionManager.saveBadge(newBadge); }); } // 仅前台时手动弹本地通知 if (notification != null && androidNoti != null) { flutterLocalNotificationsPlugin.show( notification.hashCode, notification.title, notification.body, NotificationDetails( android: AndroidNotificationDetails( channel.id, channel.name, importance: Importance.high, priority: Priority.high, playSound: true, icon: '@mipmap/ic_launcher', ), iOS: const IOSNotificationDetails(), ), payload: 'jump_page', // 可序列化自定义参数传入 ); } }); - 修复FCM点击回调逻辑,去掉强制非空断言,使用全局Key做跳转:
// 处理应用完全终止时点击FCM自动通知启动的场景 FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) { if (message == null) return; WidgetsBinding.instance.addPostFrameCallback((_) { // 根据消息参数判断跳转目标,示例为跳转注册页 navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => Register())); }); }); // 处理应用在后台时点击FCM自动通知的场景 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => DoneAndPaiedPagess(0))); });
- 清理
- 调整后端推送payload
推送时同时携带notification和data字段,将跳转需要的自定义参数放入data字段,示例结构:{ "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", "notification":{ "title":"Portugal vs. Denmark", "body":"great match!" }, "data":{ "jump_type":"match_detail", "match_id":"12345" } } - 补全权限与清单配置
- 安卓端:找到
AndroidManifest.xml中的MainActivity配置,添加android:exported="true"属性(安卓12+必须配置);在应用启动时申请安卓13+的POST_NOTIFICATIONS运行时权限。 - iOS端:应用启动时调用
FirebaseMessaging.instance.requestPermission()申请通知权限,不要在初始化本地通知时关闭权限申请开关。
- 安卓端:找到
内容的提问来源于stack exchange,提问作者Ahmed Elsayed
相关产品推荐
相关产品推荐

