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) { // 原有错误处理逻辑 } }
验证建议
- 清除浏览器缓存和已注册的Service Worker后重新测试首次启动
- 通过浏览器DevTools的
Application -> Service Workers面板,观察SW的注册激活状态 - 在关键节点添加日志,记录SW状态和Token获取的时间线,便于排查时序问题
内容的提问来源于stack exchange,提问作者Nicolas Le Bot
相关产品推荐
相关产品推荐

