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

Flutter Web集成FCM重复推送及多Service Worker问题排查

FCM重复推送问题排查与解决

我正在开发一款面向PWA(需在移动设备安装)的Web应用,原本计划做原生iOS应用,因上架问题转为全Flutter Web开发。集成FCM后,多设备(包括PC端Edge)都出现重复接收推送的问题。

现有代码情况

index.html中的Service Worker注册脚本

<script>
   if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('/firebase-messaging-sw.js');
      });
  }
</script>

firebase-messaging-sw.js配置代码

importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging-compat.js');

const firebaseConfig = {
  apiKey: "xxxx",
  authDomain: "xxxxx",
  databaseURL: "xxxx",
  projectId: "xxxxx",
  storageBucket: "xxxx",
  messagingSenderId: "xxxxx",
  appId: "xxxxxx"
};

firebase.initializeApp(firebaseConfig);

const messaging = firebase.messaging();

let darkPatternsValue = 0;

self.addEventListener('message', function(event){
  if (event.data && event.data.type === 'SET_DARK_PATTERNS_VALUE') {
    darkPatternsValue = event.data.value;
  }
});

messaging.onBackgroundMessage(function(payload) {
    console.log('Received background message ', payload);
    if(darkPatternsValue != 1){
      return;
    }
    lastNotificationId = currentNotificationId;

    const notificationTitle = payload.notification.title;
    const notificationOptions = {
        body: payload.notification.body,
      };

    return self.registration.showNotification(notificationTitle, notificationOptions);
});

self.addEventListener('notificationclick', function(event) {
    console.log('Notification clicked ', event);
    event.notification.close();
    event.waitUntil(
        clients.openWindow('/?source=notification')
    );
 });

Flutter中向Service Worker发送配置的代码

void sendDarkPatternsValue() async {
    html.window.navigator.serviceWorker?.getRegistrations().then((registrations) {
      for (final registration in registrations) {
        registration.active?.postMessage({'type': 'SET_DARK_PATTERNS_VALUE', 'value': 1});
      }
    });
  }

当前问题现象

现在存在两个运行的Service Worker:一个是根路径的http://localhost:xxxxxx/,另一个是http://localhost:xxxxxx/firebase-cloud-messaging-push-scope。

  • 当darkPatternsValue设为0时,仍会收到1条无法触发notificationclick事件的推送;
  • 当darkPatternsValue设为1时,会收到2条推送,仅第二条能触发点击事件。

问题原因

firebase-cloud-messaging-push-scope是Firebase SDK在某些场景下自动注册的专用Service Worker,用于处理推送消息。但如果手动注册了自定义的firebase-messaging-sw.js却未明确指定scope,Firebase会认为没有可用的Worker来处理推送,从而自动创建这个带push-scope的Worker,最终导致两个Worker同时监听推送事件,产生重复通知。

另外,向Worker发送darkPatternsValue时,虽然遍历了所有注册的Worker,但可能存在其中一个Worker未正确接收设置值的情况,导致部分通知不受开关控制。

解决步骤

1. 统一Service Worker注册逻辑

修改index.html中的注册代码,明确指定scope为根路径,让Firebase使用你自定义的Worker,避免自动创建额外的Worker:

<script>
  if ('serviceWorker' in navigator) {
    window.addEventListener('load', function () {
      navigator.serviceWorker.register('/firebase-messaging-sw.js', { scope: '/' });
    });
  }
</script>

或者,直接移除index.html中的手动注册代码,让Firebase SDK自动关联你的firebase-messaging-sw.js(确保文件放在根目录)。

2. 清理已存在的重复Service Worker

打开浏览器开发者工具(F12),切换到Application > Service Workers面板,找到firebase-cloud-messaging-push-scope对应的Worker,点击「Unregister」按钮手动注销。刷新页面后,确认只有根路径的Worker在运行。

3. 优化消息处理逻辑

在firebase-messaging-sw.js的onBackgroundMessage回调中,可添加消息ID的唯一性校验,避免同一消息被重复处理:

const processedMessages = new Set();

messaging.onBackgroundMessage(function(payload) {
    const messageId = payload.message_id;
    if (processedMessages.has(messageId)) return;
    processedMessages.add(messageId);

    console.log('Received background message ', payload);
    if(darkPatternsValue != 1){
      return;
    }

    const notificationTitle = payload.notification.title;
    const notificationOptions = {
        body: payload.notification.body,
      };

    return self.registration.showNotification(notificationTitle, notificationOptions);
});

4. 确保Flutter端FCM初始化正确

在Flutter Web的FCM初始化代码中,配置正确的VAPID密钥,确保SDK能正确识别自定义Service Worker:

import 'package:firebase_messaging/firebase_messaging.dart';

void initFCM() async {
  await FirebaseMessaging.instance.setAutoInitEnabled(true);
  // 替换为你的VAPID密钥,可在Firebase控制台 > 项目设置 > 云消息传递中获取
  await FirebaseMessaging.instance.getToken(vapidKey: 'YOUR_VAPID_KEY');
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 06:17:39