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

FCM通知点击跳转:Debug正常Prod环境APP关闭时页面闪退

问题解决:FCM通知点击跳转在生产环境应用关闭时页面短暂消失

问题根源

应用关闭状态下点击通知,FirebaseMessaging.instance.getInitialMessage()会触发跳转,但StartScreen的isConnected()方法里的Navigator.pushReplacement会在应用启动完成后执行——时序上通知跳转先执行,但随后被StartScreen的跳转覆盖,导致目标页面一闪而过。Debug模式下因为启动速度慢,时序刚好相反,所以没问题。

解决方案

核心思路:把通知的跳转意图先存起来,等StartScreen完成版本检查、登录状态判断的逻辑后,再执行通知跳转,避免被替换。

步骤1:新增全局变量存储通知跳转数据

在主文件顶部添加全局变量,用来保存从通知里拿到的跳转信息:

// 新增全局变量,存储通知跳转参数
Map<String, dynamic>? notificationRouteData;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

步骤2:修改getInitialMessage的处理逻辑

不再直接调用handleScreen,而是把通知数据存到全局变量里:

FirebaseMessaging.instance.getInitialMessage().then((message) {
  if(message != null) {
    // 保存通知数据,不立即跳转
    notificationRouteData = {
      "id": message.data["id"],
      "level": message.data["level"],
      "screen": message.data["screen"],
      "subject": message.data["subject"],
      "courseType": message.data["course_type"],
    };
  }
});

步骤3:修改StartScreen的isConnected方法

在版本检查和登录状态判断完成后,检查是否有未处理的通知跳转,执行跳转操作:

void isConnected(){
  WidgetsBinding.instance.addPostFrameCallback((_)async{
    String appRemoteVersion = await Utilities().remoteConfigValue("appVersion");
    if(Constants.appLocalVersion != appRemoteVersion){
      if(!mounted) return;
      Navigator.pushReplacement(
          context,
          MaterialPageRoute(
              builder: (context) => const AppNewVersionScreen()
          )
      );
    }
    else{
      Widget targetScreen;
      if(LocalData().isConnected()){
        targetScreen = const NavScreen();
      }
      else{
        targetScreen = const LoginScreen();
      }

      // 先跳转到默认页面
      if(!mounted) return;
      await Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (context) => targetScreen)
      );

      // 检查是否有通知跳转需要处理
      if(notificationRouteData != null){
        handleScreenFromNotification(notificationRouteData!);
        // 处理完清空数据,避免重复跳转
        notificationRouteData = null;
      }
    }
  });
}

步骤4:新增handleScreenFromNotification方法

把原来的handleScreen逻辑改成这个方法,注意这里用push而不是pushReplacement,避免替换掉刚跳转到的默认页面:

void handleScreenFromNotification(Map<String, dynamic> data){
  String screen = data["screen"] ?? "";

  if(screen == "premium"){
    navigatorKey.currentState?.push(
        MaterialPageRoute(
            builder: (context) => WillPopScope(
                onWillPop: () async {
                  Navigator.pushReplacement(
                      context,
                      MaterialPageRoute(
                          builder: (context) => const NavScreen()
                      )
                  );
                  return false;
                },
                child: const PremiumScreen()
            )
        )
    );
  }
  else if(screen == "aide"){
    navigatorKey.currentState?.push(
        MaterialPageRoute(
            builder: (context) => WillPopScope(
                onWillPop: () async {
                  Navigator.pushReplacement(
                      context,
                      MaterialPageRoute(
                          builder: (context) => const NavScreen()
                      )
                  );
                  return false;
                },
                child: const HelpScreen()
            )
        )
    );
  }
}

步骤5:保留其他FCM监听的原有逻辑

onMessage和onMessageOpenedApp的处理不需要修改,因为这两个场景下应用已经处于前台或后台,StartScreen的初始化逻辑已经完成,不会冲突:

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    handleScreen(message);
});

FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
  if(message.notification != null) {
    handleScreen(message);
  }
});

额外注意事项

  • 确保handleScreen方法里的跳转逻辑和handleScreenFromNotification一致,只是把pushReplacement改成push
  • 生产环境测试时,记得清理应用缓存后再测试,避免旧状态影响
  • 如果有其他页面跳转逻辑,也要注意时序问题,避免类似的替换冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 11:59:56