Flutter Web应用 - 当应用不在当前标签页时,FCM消息未被接收

移动开发 2026-07-09

我想把Firebase消息推送加入我的Flutter Web应用。

当网页应用处于活动标签时,一切正常工作。但是如果它不是活动标签,我就什么也接收不到。

下面,我附上用于捕获FCM的 JavaScript代码

我在某处看到过关于service worker未注册或相互冲突导致问题的说法。

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

firebase.initializeApp({
    apiKey: 'cccccccc',
    appId: 'ccccc',
    messagingSenderId: 'cccc',
    projectId: 'cccc',
    authDomain: 'cccc',
    storageBucket: 'cccc',
    measurementId: 'cccc',
});

const messaging = firebase.messaging();

/**
 * 🔥 CRITICAL: ensure SW becomes active immediately
 */
self.addEventListener('install', () => {
    self.skipWaiting();
});

self.addEventListener('activate', (event) => {
    event.waitUntil(self.clients.claim());
});

messaging.onBackgroundMessage((payload) => {

    const title = payload.notification?.title ?? "Notification";
    const body = payload.notification?.body ?? "";

    const route = payload.data?.route ?? "/";

    self.registration.showNotification(title, {
        body: body,
        icon: "/icons/Icon-192.png",
        data: {
            route: route
        }
    });
});

self.addEventListener("notificationclick", function(event) {

    event.notification.close();

    const route = event.notification.data?.route || "/";

    event.waitUntil(
        clients.matchAll({ type: "window", includeUncontrolled: true })
            .then((clientList) => {

                for (const client of clientList) {
                    if (client.url.includes(self.location.origin) && "focus" in client) {
                        client.focus();
                        client.navigate("/#" + route);
                        return;
                    }
                }

                return clients.openWindow("/#" + route);
            })
    );
});

解决方案

很可能是因为你使用了payload.notification,而不是一个基于数据的Web推送有效载荷。在Flutter Web中,Firebase的后台消息由service worker处理,浏览器在标签页处于非活动状态时,往往会压制或以不同的方式处理仅包含通知的消息。尝试从后端发送一个包含数据字段的有效载荷,然后在onBackgroundMessage() 中从payload.data读取这些值。你的service worker设置在其他方面是正确的,包括skipWaiting() 和clients.claim()。

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

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



firebase.initializeApp({

    apiKey: 'cccccccc',

    appId: 'cccc',

    messagingSenderId: 'cccc',

    projectId: 'cccc',

    authDomain: 'cccc',

    storageBucket: 'cccc',

    measurementId: 'cccc',

});



const messaging = firebase.messaging();



/**

 * Ensure service worker activates immediately

 */

self.addEventListener('install', () => {

    self.skipWaiting();

});



self.addEventListener('activate', (event) => {

    event.waitUntil(self.clients.claim());

});



/**

 * Handle background FCM messages

 */

messaging.onBackgroundMessage((payload) => {



    console.log('Received background message:', payload);



    const title = payload.data?.title || "Notification";

    const body = payload.data?.body || "";

    const route = payload.data?.route || "/";



    self.registration.showNotification(title, {

        body: body,

        icon: "/icons/Icon-192.png",

        data: {

            route: route

        }

    });

});



/**

 * Handle notification click

 */

self.addEventListener("notificationclick", function(event) {



    event.notification.close();



    const route = event.notification.data?.route || "/";



    event.waitUntil(

        clients.matchAll({

            type: "window",

            includeUncontrolled: true

        }).then((clientList) => {



            for (const client of clientList) {



                if (client.url.includes(self.location.origin) && "focus" in client) {

                    client.focus();

                    client.navigate("/#" + route);

                    return;

                }

            }



            return clients.openWindow("/#" + route);

        })

    );

});
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章