Flutter中DocumentSnapshot访问docs属性报错未定义的解决方法
错误原因
- 你混淆了Cloud Firestore两种快照类型的属性边界:
- 调用
collection(xxx).doc(xxx).snapshots()时,监听的是单个具体文档的变化,返回的流类型是Stream<DocumentSnapshot>,对应的snapshot.data是单个文档对象,仅代表这一个文档的数据,本身不存在docs属性。 docs属性是QuerySnapshot类型独有的,只有当你监听整个集合、或者带筛选条件的多文档查询结果时(也就是调用collection(xxx).snapshots()不通过.doc()指定单个文档),返回的QuerySnapshot才会用docs字段存储查询到的所有文档列表。
- 调用
- 你当前代码里的流定义、StreamBuilder泛型都声明为
DocumentSnapshot类型,却尝试访问只有多文档快照才有的docs属性,就会触发该getter未定义的报错。
修复方案
根据实际业务需求二选一即可:
场景1:需要读取Categories集合下ID为Pharmacy的单个文档内容
不需要遍历docs,直接读取当前DocumentSnapshot携带的数据即可,参考代码:
// 原有流定义不需要修改,保持DocumentSnapshot类型即可 @override Widget build(BuildContext context) { return StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>( stream: productsStream, builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot<Map<String, dynamic>>> snapshot) { if (snapshot.hasError) { print('Something went Wrong'); return const Center(child: Text('加载失败')); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 直接获取单个文档的数据 Map<String, dynamic>? docData = snapshot.data?.data(); if (docData == null) { return const Center(child: Text('文档不存在')); } docData['id'] = snapshot.data!.id; // 后续在这里返回你的页面组件,使用docData渲染内容 return Center(child: Text('文档ID:${docData['id']}')); } ); }
场景2:需要读取多个文档(比如Categories下所有分类、或者Pharmacy文档下的子集合文档)
需要把流的类型、StreamBuilder的泛型都改成QuerySnapshot,调整监听的引用路径到对应集合层级,这时候就可以正常访问docs属性遍历文档了,参考代码:
// 改流的类型为QuerySnapshot,路径指向你要读取的集合 // 例:读取Categories集合下所有文档 final Stream<QuerySnapshot<Map<String, dynamic>>> productsStream = FirebaseFirestore.instance .collection('Categories') // 如果要读Pharmacy下的Products子集合,就写成: // .doc('Pharmacy').collection('Products') .snapshots(); @override Widget build(BuildContext context) { // StreamBuilder泛型同步改成QuerySnapshot return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: productsStream, builder: (BuildContext context, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) { if (snapshot.hasError) { print('Something went Wrong'); return const Center(child: Text('加载失败')); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } final List productDocs = []; // 此时snapshot.data是QuerySnapshot,可正常访问docs snapshot.data!.docs.map((DocumentSnapshot document) { Map a = document.data() as Map<String, dynamic>; productDocs.add(a); a['id'] = document.id; }).toList(); // 注意:必须在这里返回渲染的Widget,原代码缺失该部分会报错 return ListView.builder( itemCount: productDocs.length, itemBuilder: (context, index) { return ListTile(title: Text(productDocs[index]['id'].toString())); } ); } ); }
注意:原代码中StreamBuilder的builder回调末尾没有返回任何Widget组件,修复时记得补上对应渲染逻辑,否则会触发Flutter的组件构建报错。
内容的提问来源于stack exchange,提问作者Archit
相关产品推荐
相关产品推荐

