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
相关产品推荐
相关产品推荐

