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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 23:36:05