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

Flutter中FCM终止状态下通知点击路由跳转异常问题

问题解决思路与代码修改

核心问题分析

  1. getInitialMessage()回调时机过早:在MyApp的initState中调用时,Get路由框架尚未完全初始化,直接调用Get.offNamed无法正确拦截启动流程,导致应用先按initialRoute进入Splash,但后续跳转逻辑与认证流程冲突,引发多次认证错误。
  2. 重复定义onBackgroundMessage:BaseScreen中再次定义后台消息处理函数,会导致FCM回调逻辑混乱。
  3. 可能的FCM Payload配置缺失:Android端未指定click_action: FLUTTER_NOTIFICATION_CLICK,导致系统直接启动主Activity,绕过Flutter路由处理。

解决方案步骤

1. 全局存储初始通知消息

在AuthController中添加变量存储初始通知消息,让SplashScreen统一处理跳转逻辑:

class AuthController extends GetxController {
  // 原有变量...
  RemoteMessage? initialNotificationMessage;

  // 原有登录状态检查方法示例
  Future<void> checkLoginStatus() async {
    // 此处替换为你的实际认证逻辑:读取本地token、验证有效性等
    await Future.delayed(const Duration(seconds: 1));
  }
}

2. 修改Main中的FCM初始化逻辑

移除getInitialMessage()中的直接跳转,改为将消息存储到全局控制器:

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    initializeFcm();
    super.initState();
  }

  Future<void> initializeFcm() async {
    FirebaseMessaging messaging = FirebaseMessaging.instance;
    
    // 仅保留一处后台消息处理定义
    FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);

    // 获取初始消息并存储,由SplashScreen统一处理
    final initialMessage = await messaging.getInitialMessage();
    if (initialMessage != null) {
      Get.find<AuthController>().initialNotificationMessage = initialMessage;
    }
  }

  // 原有build方法保持不变...
}

3. 在SplashScreen统一处理认证与跳转

确保SplashScreen先完成认证流程,再根据存储的通知消息决定后续路由:

class SplashScreen extends StatefulWidget {
  const SplashScreen({super.key});

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  @override
  void initState() {
    super.initState();
    _handleAuthAndNavigation();
  }

  Future<void> _handleAuthAndNavigation() async {
    final authController = Get.find<AuthController>();
    
    // 执行登录状态验证
    await authController.checkLoginStatus();

    if (authController.token.isNotEmpty) {
      // 已登录,根据通知消息跳转
      final message = authController.initialNotificationMessage;
      if (message != null) {
        switch (message.data['route']) {
          case 'mensagem':
            // 跳转到首页后切换到消息Tab
            Get.offNamed(PagesRoutes.baseRoute);
            // 延迟执行Tab切换,确保页面已初始化
            Future.delayed(const Duration(milliseconds: 500), () {
              final baseState = Get.find<BaseScreen>().state as _BaseScreenState;
              baseState._tabController.animateTo(3);
            });
            break;
          case 'correspondencia':
            Get.offNamed(PagesRoutes.correspondencesRoute);
            Get.find<CorrespondenceController>().getAllCorrespondences();
            break;
          default:
            Get.offNamed(PagesRoutes.baseRoute);
        }
      } else {
        // 无通知,直接跳首页
        Get.offNamed(PagesRoutes.baseRoute);
      }
    } else {
      // 未登录,跳登录页
      Get.offNamed(PagesRoutes.signInRoute);
    }
  }

  @override
  Widget build(BuildContext context) {
    // 原有Splash UI代码...
    return const Scaffold(
      body: Center(child: CircularProgressIndicator()),
    );
  }
}

4. 清理重复的FCM逻辑

删除BaseScreen中重复的onBackgroundMessage定义,避免回调冲突:

// 移除以下重复代码:
// @pragma('vm:entry-point')
// Future<void> onBackgroundMessage(RemoteMessage message) async {}

5. 确保FCM Payload配置正确(Android)

发送通知时必须包含click_action: FLUTTER_NOTIFICATION_CLICK,确保点击通知后由Flutter处理路由:

{
  "to": "用户设备Token",
  "data": {
    "route": "correspondencia",
    "click_action": "FLUTTER_NOTIFICATION_CLICK"
  },
  "notification": {
    "title": "新信件通知",
    "body": "您有一封新信件待查看"
  }
}

关键说明

  • 让SplashScreen作为唯一入口路由,统一处理认证和通知跳转,彻底避免未认证状态下直接进入业务页面引发的错误。
  • 延迟切换BaseScreen的Tab,确保页面完全初始化后再执行操作,避免空指针异常。

内容的提问来源于stack exchange,提问作者Jefferson Santos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 12:59:56