Flutter项目使用StreamBuilder读取FirebaseFirestore数据运行报错如何解决
问题描述
我在代码中使用StreamBuilder,运行后出现异常。我正在基于FirebaseFirestore开发项目,通过StreamBuilder配合ListView展示数据,编写代码时无报错,但运行应用后出现陌生异常,无法识别错误类型也不知道该如何解决。我的StreamBuilder从Products集合下的各个文档拉取数据,通过Card和Column组件处理渲染数据,该错误仅在当前项目出现,我运行其他项目一切正常,这对我来说是个很严重的问题。
问题代码
import 'dart:html'; import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/rendering.dart'; class UserScreen extends StatefulWidget { UserScreen({Key? key}) : super(key: key); @override _UserScreenState createState() => _UserScreenState(); } class _UserScreenState extends State<UserScreen> { final _formKey = GlobalKey<FormState>(); var _productName = ''; var _productDes = ''; var _bidPrice = ''; var _auctionDate = ''; void _trySubmit() async { final isValid = _formKey.currentState!.validate(); FocusScope.of(context).unfocus(); try { if (isValid) { _formKey.currentState?.save(); print(_productName); print(_productDes); print(_bidPrice); print(_auctionDate); print(' Good to go '); await FirebaseFirestore.instance.collection('Products').add({ 'productName': _productName, 'productDes': _productDes, 'bidPrice': _bidPrice, 'auctionDate': _auctionDate, }); } else { print('Form is not valid'); } } catch (err) { Text('Check credential'); } } bool isFabVisibale = false; void addNewProducts(BuildContext ctx) { showModalBottomSheet( isScrollControlled: true, context: ctx, builder: (_) { return GestureDetector( onTap: () {}, behavior: HitTestBehavior.opaque, child: Form( key: _formKey, child: ListView( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 40), addRepaintBoundaries: true, children: <Widget>[ Center( child: Text( 'Add New Product', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor), )), TextFormField( onSaved: (value) { _productName = value!; }, validator: (value) { if (value!.isEmpty) { return 'Enter Product Name'; } return null; }, keyboardType: TextInputType.emailAddress, decoration: InputDecoration( labelText: 'Product Name', icon: Icon(Icons.insert_chart)), ), TextFormField( onSaved: (value) { _productDes = value!; }, validator: (value) { if (value!.isEmpty) { return 'Enter Product Description'; } return null; }, decoration: InputDecoration( labelText: 'Product Description', icon: Icon(Icons.description)), ), TextFormField( onSaved: (value) { _bidPrice = value!; }, validator: (value) { if (value!.isEmpty) { return 'Enter Product Price'; } return null; }, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Minimum Bid Price', icon: Icon(Icons.price_check)), ), TextFormField( onSaved: (value) { _auctionDate = value!; }, validator: (value) { if (value!.isEmpty) { return 'Enter Product End Date'; } return null; }, keyboardType: TextInputType.emailAddress, decoration: InputDecoration( labelText: 'Auction End DateTime', icon: Icon(Icons.data_saver_off)), ), SizedBox( height: 20, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => UserScreen())); }, child: Text('Cancel'), ), ElevatedButton( onPressed: _trySubmit, child: Text('Add Product'), ), ], ), ], ), ), ); }, ); } @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( appBar: AppBar( title: Text('User Screen'), actions: [ DropdownButton( items: [ DropdownMenuItem( child: Container( child: Row( children: <Widget>[ Icon( Icons.exit_to_app, color: Theme.of(context).indicatorColor, ), SizedBox( width: 8, ), Text('Logout') ], ), ), value: 'logout', ), ], onChanged: (itemIdentifire) { if (itemIdentifire == 'logout') { FirebaseAuth.instance.signOut(); } }, ), ], ), body: NotificationListener<UserScrollNotification>( onNotification: (notification) { if (notification.direction == ScrollDirection.forward) { setState(() { isFabVisibale = true; }); } if (notification.direction == ScrollDirection.reverse) { setState(() { isFabVisibale = false; }); } return true; }, child: StreamBuilder( stream: FirebaseFirestore.instance.collection('Products').snapshots(), builder: (BuildContext contex, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) { if (!snapshot.hasData) { return Text('No Produts for display..'); } return ListView( physics: ScrollPhysics(), children: snapshot.data!.docs.map((document) { return Card( child: Row( children: <Widget>[ Card( child: Text('Image'), ), Column(children:<Widget> [ Text('Name :' + document['productName']), Text('description :' + document['productDes']), Text('Price :' + document['bidPrice']), Text('Date :' + document['auctionDate']), TextButton(onPressed: (){}, child: Text('Bid')), ],), ], ), ); }).toList(), ); }, ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: isFabVisibale ? FloatingActionButton( child: Icon(Icons.add), onPressed: () { addNewProducts(context); }, ) : null, ), ); } }
异常信息

修复方案
- 删除开头的
import 'dart:html';导入,该包仅适用于Flutter Web项目,移动端项目导入会直接触发运行异常。 - 完善StreamBuilder的状态判断逻辑,补充错误、加载状态处理:
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) { // 先处理错误状态 if (snapshot.hasError) { return Text('加载失败:${snapshot.error}'); } // 处理加载中状态 if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } // 处理空数据状态 if (!snapshot.hasData || snapshot.data!.docs.isEmpty) { return Text('暂无产品可展示'); } // 正常渲染数据 return ListView( // ...原有逻辑 ); } - 访问Firestore文档字段时添加空安全校验,避免部分文档缺少字段触发空异常:
Text('Name :' + (document.get('productName') ?? '')), Text('description :' + (document.get('productDes') ?? '')), Text('Price :' + (document.get('bidPrice') ?? '')), Text('Date :' + (document.get('auctionDate') ?? '')), - 修复模态框取消按钮的逻辑,替换原重复跳转UserScreen的代码为关闭模态框:
onPressed: () { Navigator.of(context).pop(); }, - 补充_trySubmit方法catch块的错误处理,新增错误打印和用户提示:
catch (err) { print('提交错误:$err'); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('提交失败,请检查权限配置'))); } - 检查Firebase Firestore安全规则,确保当前用户有Products集合的读取权限,测试阶段可临时开放权限验证问题:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /Products/{product} { allow read, write: if request.auth != null; } } } - 可顺便修复代码中的拼写错误:
isFabVisibale改为isFabVisible,No Produts改为No Products,避免后续维护问题。
内容的提问来源于stack exchange,提问作者Md. Fazle Rabbi
相关产品推荐
相关产品推荐

