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

iOS Flutter集成FCM重启重复接收旧通知问题求助

解决FCM重启App重复推送旧通知的问题

问题原因

在iOS平台上,FCM通知分为显示通知(带alert字段)和静默数据通知两类。如果是显示通知,实际由APNs负责推送,若未正确标记通知已读,或是App初始化时重复触发旧通知的处理逻辑,就会导致重复接收。另外你的代码存在两处隐患:前台监听onMessage时直接调用后台处理函数,以及后台处理逻辑里重复初始化Firebase,这些都可能引发重复推送问题。

具体解决步骤

1. 给通知添加“已处理”标记,避免重复执行逻辑

用SharedPreferences存储已处理过的通知ID,每次处理通知前先检查是否已处理,处理完成后标记状态,防止重启后重复触发:

// 先在pubspec.yaml添加依赖:shared_preferences: ^2.2.2
import 'package:shared_preferences/shared_preferences.dart';

// 检查通知是否已处理
Future<bool> isMessageProcessed(String? messageId) async {
  if (messageId == null) return false;
  SharedPreferences prefs = await SharedPreferences.getInstance();
  return prefs.getBool('processed_$messageId') ?? false;
}

// 标记通知为已处理
Future<void> markMessageProcessed(String? messageId) async {
  if (messageId == null) return;
  SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.setBool('processed_$messageId', true);
}

2. 优化Firebase初始化与通知监听逻辑

修改初始化代码,去掉冗余的Firebase初始化操作,同时正确处理前台、后台以及App启动时的通知:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await MyDbModel().initializeDB();
  await initFireBase(); // 用await确保Firebase初始化完成再启动App
  runApp(myApp());
}

void initFireBase() async {
  print("------------- InitFireBase -------------");
  await Firebase.initializeApp();

  // 请求iOS通知权限(必须步骤)
  await FirebaseMessaging.instance.requestPermission(
    alert: true,
    badge: true,
    sound: true,
  );

  // 处理前台收到的通知
  FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
    if (!await isMessageProcessed(message.messageId)) {
      await updateDB(message);
      await markMessageProcessed(message.messageId);
      // iOS前台默认不弹通知,需要调用本地通知插件展示
      await showLocalNotification(message);
    }
  });

  // 处理App从杀死状态点击通知启动的场景
  RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
  if (initialMessage != null) {
    if (!await isMessageProcessed(initialMessage.messageId)) {
      await updateDB(initialMessage);
      await markMessageProcessed(initialMessage.messageId);
    }
  }

  // 注册后台消息处理回调
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}

// 单独抽离DB更新逻辑,和通知处理解耦
Future<void> updateDB(RemoteMessage message) async {
  // 这里写你的数据库更新代码
}

// 后台消息处理函数:去掉重复的Firebase初始化
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  if (!await isMessageProcessed(message.messageId)) {
    await updateDB(message);
    await markMessageProcessed(message.messageId);
  }
}

// 前台展示本地通知的方法(iOS专属)
Future<void> showLocalNotification(RemoteMessage message) async {
  final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  // 初始化iOS通知设置
  const iosSettings = IOSInitializationSettings();
  await flutterLocalNotificationsPlugin.initialize(const InitializationSettings(iOS: iosSettings));

  if (message.notification != null) {
    await flutterLocalNotificationsPlugin.show(
      message.messageId.hashCode,
      message.notification?.title,
      message.notification?.body,
      const NotificationDetails(
        iOS: IOSNotificationDetails(),
      ),
    );
  }
}

3. 关键注意事项

  • 删掉后台处理函数中的await Firebase.initializeApp();:后台消息触发时Firebase已完成初始化,重复调用会引发状态异常;
  • iOS前台通知需手动展示:APNs默认不在App前台弹出通知,必须通过本地通知插件实现;
  • 必须用messageId做唯一标识:每个FCM通知都有唯一的messageId,以此判断是否已处理是防止重复的核心;
  • initFireBase要加await:确保Firebase初始化完成后再启动App,避免启动时漏处理通知状态。

总结

核心解决思路就是给每个通知加唯一标识并标记处理状态,同时优化Firebase的初始化和通知监听逻辑,避免重复执行处理代码,这样重启App后就不会再收到旧通知了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 05:45:33