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]
现象说明
- 异常状态:加载后无数据显示
- 正常状态(移除字段后):数据正常加载显示
问题原因及解决方案
核心原因
- 字段类型/空值不兼容:Firestore中部分文档的
incomeTotal/expenseTotal可能缺失、类型与模型类的int不匹配(如存为double),导致fromJson解析失败,但Stream未捕获该异常,直接中断数据传递。 - 错误处理缺失: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
相关产品推荐
相关产品推荐

