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

Flutter Firebase通知问题:重启应用后重复打开文章

问题:Flutter FCM通知导致重启应用自动打开历史文章

在Flutter应用的Firebase Cloud Messaging(FCM)通知功能中出现异常:从通知打开文章后,完全关闭应用再手动重启,应用会再次打开同一文章而非默认首页。

问题重现步骤

  • 应用处于后台;
  • 收到新文章的推送通知;
  • 用户点击通知,应用正确打开对应文章;
  • 用户完全关闭应用;
  • 用户手动重启应用,未点击通知;
  • 异常:应用仍打开之前通知对应的文章。

预期行为:手动重启应用应启动到首页(默认页面),而非自动跳转至历史通知的文章。

排查分析

  • SharedPreferences中存储的待处理文章ID未被正确清除;
  • 未正确处理FirebaseMessaging.instance.getInitialMessage(),导致冷启动时错误读取了历史通知数据。

解决方案

1. 区分冷启动与后台唤醒的通知处理

onMessageOpenedApp仅处理后台唤醒的通知点击,而冷启动(完全关闭后重启)的通知点击需要通过getInitialMessage()处理。之前的代码未处理冷启动场景,且重复监听onMessageOpenedApp导致逻辑混乱。

2. 优化SharedPreferences的使用逻辑

仅在必要场景下临时存储跳转标识,且跳转完成后立即清除,避免残留数据影响下次启动。

修改后的核心代码

修正main函数逻辑

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  String? initialArticleId;

  // 处理冷启动(完全关闭后从通知打开)
  final initialMessage = await FirebaseMessaging.instance.getInitialMessage();
  if (initialMessage != null && initialMessage.data['post_id'] != null) {
    initialArticleId = initialMessage.data['post_id'];
  }

  // 处理后台唤醒时的通知点击
  FirebaseMessaging.onMessageOpenedApp.listen((message) async {
    if (message.data['post_id'] != null) {
      // 直接通过全局导航键跳转,无需存入SharedPreferences
      Navigator.of(navigatorKey.currentContext!).pushNamed(
        '/article',
        arguments: message.data['post_id'],
      );
    }
  });

  await NotificationController().initialize();

  runApp(
    ChangeNotifierProvider(
      create: (_) => ThemeProvider(),
      child: MyApp(initialArticleId: initialArticleId),
    ),
  );
}

优化首页跳转逻辑

在MyApp中根据冷启动传入的文章ID决定初始路由,同时清除可能的残留数据:

// 提前定义全局导航键
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

class MyApp extends StatelessWidget {
  final String? initialArticleId;

  const MyApp({super.key, this.initialArticleId});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      initialRoute: initialArticleId != null ? '/article' : '/home',
      routes: {
        '/home': (context) => const HomePage(),
        '/article': (context) => ArticlePage(
              id: ModalRoute.of(context)?.settings.arguments as String,
            ),
      },
      onGenerateRoute: (settings) {
        // 跳转文章页后立即清除SharedPreferences中的残留数据
        if (settings.name == '/article') {
          SharedPreferences.getInstance().then((prefs) {
            prefs.remove('pending_article_id');
          });
        }
        return null;
      },
    );
  }
}

废弃原checkPendingArticle逻辑

原逻辑依赖SharedPreferences存储跳转ID,容易导致残留,改为直接通过getInitialMessage()获取冷启动的通知数据,无需中间存储。

关键注意事项

  • 必须使用全局导航键(navigatorKey),避免在onMessageOpenedApp回调中获取上下文失败;
  • getInitialMessage()仅在冷启动时返回一次通知数据,不会重复触发;
  • 彻底移除原代码中通过SharedPreferences存储pending_article_id的逻辑,杜绝残留数据干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 05:42:46