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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:31:09