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

如何从Firebase Service Worker向Flutter Web应用传递FCM后台消息数据

Fixing FCM Background Messaging Data Transfer in Flutter Web

Hey there! Let's work through your FCM background messaging issues step by step—you're on the right track with using a Service Worker, but there are a few key fixes needed to get data passed to your Flutter app properly.

First: Resolve the importScripts Undefined Error

This usually happens if your Service Worker file isn't placed correctly or registered properly in Flutter Web:

  • Make sure your firebase-messaging-sw.js is in the root of your web folder (same directory as index.html). Flutter serves files from this root in production, so placing it here ensures it's accessible.
  • In your index.html, add this script to register the Service Worker (place it before the Flutter entry script):
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
          navigator.serviceWorker.register('/firebase-messaging-sw.js')
            .then(registration => {
              console.log('Service Worker registered:', registration);
            })
            .catch(err => {
              console.error('Service Worker registration failed:', err);
            });
        });
      }
    </script>
    
  • Note: In Flutter's development mode, you might need to run with flutter run -d chrome --web-port 5000 (or another port) to avoid Service Worker caching issues, and ensure Chrome allows local Service Workers (check chrome://flags/#allow-insecure-localhost if needed).

Second: Properly Transfer Data from Service Worker to Flutter

You can't modify client.state or use window in a Service Worker—postMessage() is the only safe way to send data to your Flutter app. Here's how to fix your Service Worker code and set up Flutter to listen for these messages:

Updated Service Worker Code

importScripts("https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.6.1/firebase-messaging.js");

firebase.initializeApp({
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID",
  measurementId: "YOUR_MEASUREMENT_ID",
});

const messaging = firebase.messaging();

// Handle background messages
messaging.onBackgroundMessage((m) => {
  const type = m.data.type;
  const id = m.data.id;

  console.log(`Received background message: ${type} | ID: ${id}`);

  // Send data to all active Flutter clients
  clients.matchAll({ type: "window", includeUncontrolled: true })
    .then(clientList => {
      clientList.forEach(client => {
        // Send the actual data via postMessage
        client.postMessage({
          type: type,
          id: id,
          messageType: 'backgroundFCM' // Add identifier to distinguish FCM messages
        });
      });
    });

  // Show notification with attached data
  return self.registration.showNotification(
    m.data.title,
    {
      body: m.data.body || m.data.type,
      data: { type: type, id: id } // Attach data for click handling
    }
  );
});

// Handle notification clicks
self.addEventListener("notificationclick", (event) => {
  event.notification.close();
  
  // Activate Flutter app and send click data
  event.waitUntil(
    clients.matchAll({ type: "window", includeUncontrolled: true })
      .then(clientList => {
        // Find existing Flutter client or open new one
        const client = clientList.find(c => c.url.includes(window.location.origin));
        if (client) {
          client.focus();
          client.postMessage({
            type: event.notification.data.type,
            id: event.notification.data.id,
            messageType: 'notificationClick'
          });
        } else {
          clients.openWindow('/').then(newClient => {
            // Wait for new client to load before sending data
            setTimeout(() => {
              newClient.postMessage({
                type: event.notification.data.type,
                id: event.notification.data.id,
                messageType: 'notificationClick'
              });
            }, 1000);
          });
        }
      })
  );
});

Flutter Side: Listen for Messages from Service Worker

Use dart:html to listen for the message event from the Service Worker, then pass the data to your Flutter business logic with a StreamController:

import 'dart:html' as html;
import 'dart:async';
import 'package:flutter/material.dart';

// Stream to broadcast FCM messages across your app
final StreamController<Map<String, dynamic>> _fcmBackgroundStream = StreamController.broadcast();
Stream<Map<String, dynamic>> get fcmBackgroundStream => _fcmBackgroundStream.stream;

void initFCMBackgroundListener() {
  // Listen for messages from the Service Worker
  html.window.addEventListener('message', (html.Event event) {
    if (event is html.MessageEvent) {
      final data = event.data as Map<String, dynamic>;
      if (data['messageType'] != null) {
        _fcmBackgroundStream.add(data);
      }
    }
  });
}

void main() {
  initFCMBackgroundListener();
  runApp(const MyApp());
}

// Example widget using the FCM stream
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('FCM Background Test')),
        body: StreamBuilder<Map<String, dynamic>>(
          stream: fcmBackgroundStream,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              final message = snapshot.data!;
              return Center(
                child: Text(
                  '${message['messageType']}\nType: ${message['type']}\nID: ${message['id']}',
                  textAlign: TextAlign.center,
                ),
              );
            }
            return const Center(child: Text('Waiting for background messages...'));
          },
        ),
      ),
    );
  }
}

Key Reminders

  • Service Workers are isolated: They can't access Flutter app state directly—only use postMessage() to communicate.
  • Notification data: Always attach type and id to the notification's data field to retrieve it on click.
  • Client targeting: When sending messages, verify the client.url matches your app's origin to ensure you're talking to the right Flutter instance.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 06:12:28