Flutter Web偶发Cloud Firestore网络问题排查方法咨询
记录与排查Flutter Web + Firestore偶发WebChannel网络错误
问题背景
- 部署在Firebase Hosting的Flutter Web应用,基于Cloud Firestore存储数据
- 部分使用Windows 10 Pro系统、Edge浏览器的用户反馈:表单偶尔卡顿无法保存,频率约为每日或每周一次
- 临时修复方式:关闭并重启浏览器后应用恢复正常(用户凭据本地存储,重启后自动登录)
- 错误特征:控制台显示Firestore(9.9.0) Connection WebChannel transport网络错误,后续触发连锁报错
- 已排除项:Firebase Auth令牌过期(测试PC休眠8小时后应用仍可正常运行)
核心需求:如何记录并排查这类偶发的网络通信错误?
一、错误记录强化方案
1. 开启Firestore详细日志与连接状态监听
在应用初始化阶段添加以下代码,捕获Firestore底层连接细节:
// 开启Firestore日志输出 FirebaseFirestore.instance.settings = const Settings( persistenceEnabled: true, loggingEnabled: true, ); // 监听Firestore连接状态变化,记录离线/在线事件 FirebaseFirestore.instance.collection('__connect__').snapshots().listen((snapshot) { final status = snapshot.metadata.isFromCache ? "离线" : "在线"; debugPrint('Firestore连接状态:$status'); // 可将状态上传至Firebase Analytics或自定义日志系统 });
2. 全局异常捕获与上报
针对WebChannel相关错误做特殊处理,收集用户环境信息并上报:
FlutterError.onError = (FlutterErrorDetails details) { if (details.exception.toString().contains('WebChannel')) { FirebaseCrashlytics.instance.recordError( details.exception, details.stack, reason: 'Firestore WebChannel传输错误', information: [ '用户代理:${html.window.navigator.userAgent}', '时间戳:${DateTime.now().toIso8601String()}', ], ); } FlutterError.presentError(details); };
3. 用户端日志导出功能
在表单提交失败时,增加日志导出入口,将控制台日志、Firestore连接状态、用户信息打包成文本,方便用户反馈问题。
二、排查方向与修复建议
1. WebChannel连接稳定性排查
- 该错误通常与WebSocket连接中断相关,检查用户网络环境是否存在代理、VPN、防火墙限制Firebase域名的情况
- 确认Edge浏览器WebSocket功能未被禁用:在地址栏输入
edge://flags/#enable-websocket,确保选项处于启用状态
2. 升级Firestore SDK版本
当前使用的Firestore 9.9.0存在已知WebChannel连接问题,建议升级至最新稳定版,官方已修复多个相关bug。
3. 优化应用后台重连逻辑
添加页面可见性监听,在应用从后台激活时手动触发Firestore连接恢复:
html.document.addEventListener('visibilitychange', (event) { if (!html.document.hidden) { FirebaseFirestore.instance.clearPersistence(); FirebaseFirestore.instance.enableNetwork(); } });
4. 表单提交逻辑优化
- 移除无效的
Future.delayed(未使用await,无实际延迟效果) - 添加提交超时处理,避免无限等待:
await FirebaseFirestore.instance.collection(kQptFireStoreEventCollection) .add(schedulerEventMap) .timeout(const Duration(seconds: 10), onTimeout: () { throw TimeoutException('提交超时,请检查网络后重试'); });
附:相关信息
Flutter Doctor输出
[✓] Flutter (Channel stable, 3.3.4, on macOS 13.1 22C65 darwin-arm, locale en-US) • Flutter版本3.3.4,稳定版通道,路径/Users/XXX/Development/flutter • 上游仓库为Flutter官方仓库 • Framework修订版本eb6d86ee27(5个月前,2022-10-04 22:31:45 -0700) • Engine修订版本c08d7d5efc • Dart版本2.18.2 • DevTools版本2.15.0 [✓] Android工具链 - 开发Android设备(Android SDK版本31.0.0) • Android SDK路径/Users/XXX/Library/Android/sdk • 平台android-31,build-tools 31.0.0 • Java二进制文件路径/Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java • Java版本OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301) • 所有Android许可证已接受 [✓] Xcode - 开发iOS和macOS应用(Xcode 14.2) • Xcode路径/Applications/Xcode.app/Contents/Developer • Build版本14C18 • CocoaPods版本1.11.3 [✓] Chrome - 开发Web应用 • Chrome路径/Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio(版本2022.1) • Android Studio路径/Applications/Android Studio.app/Contents • Flutter插件可通过JetBrains插件市场获取 • Dart插件可通过JetBrains插件市场获取 • Java版本OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301) [✓] 已连接设备(2台可用) • macOS(桌面)• macos • darwin-arm64 • macOS 13.1 22C65 darwin-arm • Chrome(Web)• chrome • web-javascript • Google Chrome 110.0.5481.100 [✓] HTTP主机可用性 • 所有必需的HTTP主机均可用 • 未发现问题!
表单提交核心代码
onPressed: () async { if (_formKey.currentState!.validate()) { setState(() { _submitBtnText = 'Saving'; }); try { final schedulerEventMap = SchedulerEvent( title: '${_firstNameFormFieldController.text} ${_lastNameFormFieldController.text}', cemeteryLocation: targetEvent.cemeteryLocation, fname: _firstNameFormFieldController.text, lname: _lastNameFormFieldController.text, from: targetEvent.from, to: targetEvent.to, isCremated: targetEvent.isCremated, isVeteran: targetEvent.isVeteran, veteranBranch: targetEvent.veteranBranch, famServDir: _famServicesFormFieldController.text, funeralHome: targetEvent.showFuneralHomeList ? targetEvent.funeralHome : _funeralHomeFormFieldController.text, showFuneralHomeList: targetEvent.showFuneralHomeList, parish: targetEvent.showParishList ? targetEvent.parish : _parishFormFieldController.text, showParishList: targetEvent.showParishList, funeralServiceDateTime: targetEvent.funeralServiceDateTime, hasReception: targetEvent.hasReception, hasEscort: targetEvent.hasEscort, burialLocation: _burialLocationFormFieldController.text, showCommittalList: targetEvent.showCommittalList, placeOfCommittal: targetEvent.showCommittalList ? targetEvent.placeOfCommittal : _committalFormFieldController.text, showObcList: targetEvent.showObcList, outerBurialContainer: targetEvent.showObcList ? targetEvent.outerBurialContainer : _obcFormFieldController.text, obcOrderedBy: _obcOrderedByFormFieldController.text, obcOrderedDt: targetEvent.obcOrderedDt, hasWitness: targetEvent.hasWitness, removeMemorial: targetEvent.removeMemorial, flaggedBy1st: _flaggedBy1stFormFieldController.text, flaggedBy2nd: _flaggedBy2ndFormFieldController.text, blindCheckBy: _blindCheckByFormFieldController.text, groundStaffPrepSite: _groundStaffSitePrepFormFieldController.text, groundStaffCommittal: _groundStaffCommittalFormFieldController.text, cemeteryRepAtCommittal: _cemeteryRepCommittalFormFieldController.text, isPending: _isPending, createdDate: DateTime.now(), createdBy: '${_currentUser.fname} ${_currentUser.lname}', background: SchedulerUtils.setBackgroundColorForFirestore(targetEvent.cemeteryLocation!), isAllDay: false, eventType: kQptEventTypeFuneral) .toFireStore(); if (_notesFormFieldController.text.isNotEmpty) { schedulerEventMap['notes'] = FieldValue.arrayUnion([ { "date": DateTime.now(), "notes": _notesFormFieldController.text, "user": _currentUser, }, ]); } await FirebaseFirestore.instance.collection(kQptFireStoreEventCollection).add(schedulerEventMap); formSubmit("Submitted without issues!"); } on Exception catch (e) { formSubmit("Error submitting your event! \n${e.toString()}"); } }
用户端错误说明
用户在开发者控制台观察到的错误为Firestore(9.9.0) Connection WebChannel transport网络错误,该错误会引发后续连锁报错。
内容的提问来源于stack exchange,提问作者Martin Palmer
相关产品推荐
相关产品推荐

