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

Flutter Web首次启动报No Active Service Worker错误求助

Flutter Web FCM Token首次获取失败(无活跃Service Worker)解决方案

问题现象

升级至Flutter 3.22.2后,Web应用首次启动调用_firebaseMessaging.getToken()时触发错误:AbortError: Failed to execute 'subscribe' on 'PushManager': Subscription failed - no active Service Worker,第二次启动恢复正常,本地、预生产、生产环境均存在该问题。

核心原因

首次启动时,Flutter应用初始化完成时间早于Service Worker注册并激活的时间,导致调用FCM Token接口时,Service Worker尚未处于活跃状态,无法完成Push订阅流程。

解决方案

方案1:Dart代码中等待Service Worker激活后再获取Token

通过JS互调机制,在Dart中先确认Service Worker已激活,再执行Token获取操作:

import 'dart:js' as js;

// 检查Service Worker是否活跃
Future<bool> _isServiceWorkerActive() async {
  if (!js.context.hasProperty('navigator') || 
      !js.context['navigator'].hasProperty('serviceWorker')) {
    return false;
  }
  final registration = await js.context['navigator']['serviceWorker']['ready'].toFuture();
  return registration != null;
}

// 修改Token获取逻辑
Future<String?> getFcmToken() async {
  final vapidKeyPreprod = '${dotenv.env['PREPROD_VAPID_KEY_WEB']}';
  final vapidKeyProd = '${dotenv.env['PROD_VAPID_KEY_WEB']}';
  final isProd = DefaultFirebaseOptions.currentPlatformFlavors?.prod != null ? false : true;
  final vapidKey = isProd ? vapidKeyProd : vapidKeyPreprod;

  try {
    // 等待Service Worker激活,最多等待10秒
    final swActive = await Future.wait([
      _isServiceWorkerActive(),
      Future.delayed(const Duration(seconds: 10), () => false)
    ]).then((results) => results.first);

    if (!swActive) {
      print('Service Worker未在规定时间内激活');
      return null;
    }

    final fcmToken = await _firebaseMessaging.getToken(
      vapidKey: currentPlatform.isWeb ? vapidKey : null,
    );

    return fcmToken;
  } catch (e, stackTrace) {
    print(e);
    errorReportingService.reportException(e, stackTrace: stackTrace);
    return null;
  }
}

方案2:调整index.html中Service Worker注册时机,优先于Flutter初始化

将flutter_bootstrap.js改为动态加载,确保Service Worker激活后再启动Flutter:

<body>
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', async () => {
        try {
          // 注册并等待Service Worker激活
          const registration = await navigator.serviceWorker.register('firebase-messaging-sw.js');
          await navigator.serviceWorker.ready;
          
          // SW激活后加载Flutter
          const script = document.createElement('script');
          script.src = 'flutter_bootstrap.js';
          document.body.appendChild(script);
        } catch (error) {
          console.error('Service Worker注册失败:', error);
          // 注册失败仍加载Flutter
          const script = document.createElement('script');
          script.src = 'flutter_bootstrap.js';
          document.body.appendChild(script);
        }
      });
    } else {
      console.error('浏览器不支持Service Worker');
      // 直接加载Flutter
      const script = document.createElement('script');
      script.src = 'flutter_bootstrap.js';
      document.body.appendChild(script);
    }
  </script>
</body>

方案3:通过Service Worker发送激活通知,Dart端监听后再获取Token

修改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 = {
    // 你的Firebase配置
};

firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

// SW激活后向页面发送通知
self.addEventListener('activate', (event) => {
  event.waitUntil(
    clients.matchAll({ type: 'window' }).then((clientList) => {
      clientList.forEach((client) => {
        client.postMessage({ type: 'SW_ACTIVATED' });
      });
    })
  );
});

修改Dart代码监听激活通知:

import 'dart:js' as js;
import 'dart:async';

final _swActivatedCompleter = Completer<bool>();

// 初始化时监听SW激活消息
void initSwListener() {
  if (currentPlatform.isWeb) {
    js.context['addEventListener']('message', (js.JsEvent event) {
      if (event['data']['type'] == 'SW_ACTIVATED') {
        _swActivatedCompleter.complete(true);
      }
    });
  }
}

// 修改Token获取逻辑
Future<String?> getFcmToken() async {
  // 原有vapidKey配置逻辑
  
  try {
    // 等待SW激活通知或超时
    final swActivated = await Future.wait([
      _swActivatedCompleter.future,
      Future.delayed(const Duration(seconds: 10), () => false)
    ]).then((results) => results.first);

    if (!swActivated) {
      print('未收到Service Worker激活通知');
      return null;
    }

    final fcmToken = await _firebaseMessaging.getToken(
      vapidKey: currentPlatform.isWeb ? vapidKey : null,
    );

    return fcmToken;
  } catch (e, stackTrace) {
    // 原有错误处理逻辑
  }
}

验证建议

  1. 清除浏览器缓存和已注册的Service Worker后重新测试首次启动
  2. 通过浏览器DevTools的Application -> Service Workers面板,观察SW的注册激活状态
  3. 在关键节点添加日志,记录SW状态和Token获取的时间线,便于排查时序问题

内容的提问来源于stack exchange,提问作者Nicolas Le Bot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:23:15