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

Flutter中StreamBuilder加载Firebase数据偶尔失效,新增字段引发问题

问题描述

Flutter项目中使用StreamBuilder与Firebase Firestore交互时,数据偶尔无法显示。新增incomeTotal和expenseTotal字段前一切正常,移除这两个字段后数据可正常加载;曾尝试将字段名改为income,当时能正常运行,但之后问题再次出现两次。


相关代码

读取数据函数

Stream<List<CardDetails>> readCards() => FirebaseFirestore.instance
      .collection('cards')
      .snapshots()
      .map((snapshot) => snapshot.docs
          .map((doc) => CardDetails.fromJson(doc.data()))
          .toList());

StreamBuilder加载数据

StreamBuilder<List<CardDetails>>(
    stream: readCards(),
    builder: (context, snapshot) {
      if (!snapshot.hasData) {
        return const Center(
            child: CircularProgressIndicator());
      } else if (snapshot.hasError) {
        return const Text("error");
      } else if (snapshot.hasData &&
          snapshot.data!.isEmpty) {
        return Container(
          margin: const EdgeInsets.all(10),
          width: double.infinity,
          decoration: BoxDecoration(
              color:
                  const Color.fromARGB(50, 0, 0, 0),
              borderRadius:
                  BorderRadius.circular(20)),
          child: Transform.scale(
              scale: 1.5,
              child: Lottie.asset("lib/images/space_animation.json")),
        );
      } else {
        final cards = snapshot.data!;
        if (cardIdOfficial == '') {
          WidgetsBinding.instance
              .addPostFrameCallback((_) {
            setState(() {
              cardIdOfficial = snapshot
                  .data![_pageViewController
                      .initialPage]
                  .id;
            });
          });
        }
    
        return PageView(
            padEnds: false,
            controller: _pageViewController,
            onPageChanged: (value) => {
                  setState(() {
                    cardIdOfficial =
                        snapshot.data![value].id;
                  })
                },
            physics: const ClampingScrollPhysics(),
            children:
                cards.map(buildCards).toList());
      }
    }),

CardDetails模型类

class CardDetails {
  final String id;
  final String title;
  final double balance;
  final int cardDigits;
  final int incomeTotal;
  final int expenseTotal;

  CardDetails({
    required this.id,
    required this.title,
    required this.balance,
    required this.cardDigits,
    required this.incomeTotal,
    required this.expenseTotal,
  });

  Map<String, dynamic> toJson() => {
        'id': id,
        'title': title,
        'balance': balance,
        'cardDigits': cardDigits,
        'incomeTotal': incomeTotal,
        'expenseTotal': expenseTotal,
      };
  static CardDetails fromJson(Map<String, dynamic> json) => CardDetails(
      id: json['id'],
      title: json['title'],
      balance: json['balance'],
      cardDigits: json['cardDigits'],
      incomeTotal: json['incomeTotal'],
      expenseTotal: json['expenseTotal']);
}

Flutter环境信息

Flutter doctor
[✓] Flutter (Channel stable, 3.7.3, on macOS 11.6.3 20G415 darwin-arm64, locale en-US)
[!] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    ✗ cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] IntelliJ IDEA Community Edition (version 2022.1.3)
[✓] VS Code (version 1.74.2)
[✓] VS Code (version 1.64.2)
[✓] Connected device (3 available)
[✓] HTTP Host Availability

依赖版本

dependencies:
- cloud_firestore 4.3.1 [cloud_firestore_platform_interface cloud_firestore_web collection firebase_core firebase_core_platform_interface flutter meta]
- cupertino_icons 1.0.5
- custom_refresh_indicator 2.0.1 [flutter]
- firebase_core 2.4.1 [firebase_core_platform_interface firebase_core_web flutter meta]
- fluentui_icons 1.0.0 [flutter]
- fluentui_system_icons 1.1.190 [flutter]
- flutter 0.0.0 [characters collection js material_color_utilities meta vector_math sky_engine]
- flutter_slidable 2.0.0 [flutter]
- gap 2.0.1 [flutter]
- google_fonts 3.0.1 [flutter http path_provider crypto]
- google_nav_bar 5.0.6 [flutter]
- grouped_list 5.1.2 [flutter]
- intl 0.18.0 [clock meta path]
- lottie 2.2.0 [archive flutter path vector_math]
- metaballs 1.4.2 [flutter]
- provider 6.0.5 [collection flutter nested]
- sizer 2.0.15 [universal_io flutter]
- smooth_page_indicator 1.0.0+2 [flutter]

现象说明

  • 异常状态:加载后无数据显示
  • 正常状态(移除字段后):数据正常加载显示

问题原因及解决方案

核心原因

  1. 字段类型/空值不兼容:Firestore中部分文档的incomeTotal/expenseTotal可能缺失、类型与模型类的int不匹配(如存为double),导致fromJson解析失败,但Stream未捕获该异常,直接中断数据传递。
  2. 错误处理缺失:StreamBuilder仅展示模糊的"error"文本,无法定位具体解析问题;单个文档解析失败会导致整个列表无法加载。

解决步骤

1. 修复模型类的序列化逻辑

修改fromJson方法,增加空值判断和类型兼容转换,避免解析崩溃:

static CardDetails fromJson(Map<String, dynamic> json) => CardDetails(
      id: json['id'] ?? '',
      title: json['title'] ?? '',
      balance: (json['balance'] as num?)?.toDouble() ?? 0.0,
      cardDigits: (json['cardDigits'] as num?)?.toInt() ?? 0,
      // 处理空值和类型转换,确保解析容错
      incomeTotal: (json['incomeTotal'] as num?)?.toInt() ?? 0,
      expenseTotal: (json['expenseTotal'] as num?)?.toInt() ?? 0,
    );

2. 在Stream中捕获解析异常

修改readCards函数,单独处理每个文档的解析错误,避免单个文档异常导致整个Stream中断:

Stream<List<CardDetails>> readCards() => FirebaseFirestore.instance
      .collection('cards')
      .snapshots()
      .map((snapshot) => snapshot.docs
          .map((doc) {
            try {
              return CardDetails.fromJson(doc.data());
            } catch (e) {
              // 打印错误日志,定位问题文档
              print('解析文档失败: ${doc.id}, 错误: $e');
              // 返回默认占位数据或跳过该文档,按需选择
              return CardDetails(
                id: doc.id,
                title: '解析错误',
                balance: 0.0,
                cardDigits: 0,
                incomeTotal: 0,
                expenseTotal: 0,
              );
            }
          })
          .toList());

3. 检查Firestore文档一致性

登录Firebase控制台,检查cards集合:

  • 确保所有文档都包含incomeTotal和expenseTotal字段
  • 确保字段类型统一为整数(Int),不要混合其他类型
  • 批量修复缺失/类型错误的文档

4. 优化错误提示

修改StreamBuilder的错误显示,展示具体错误信息便于调试:

else if (snapshot.hasError) {
  return Text("加载失败: ${snapshot.error.toString()}");
}

额外建议

  • 升级Firebase依赖到最新稳定版,规避旧版本已知bug
  • 使用json_serializable库自动生成序列化代码,减少手动解析错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:05:23