Flutter中如何通过Firestore的bookmark集合文档ID查询question集合文档
解决方案
方案1:嵌套StreamBuilder(实现简单,适合书签数量少的场景)
你可以直接在列表项中嵌套StreamBuilder实现需求,外层StreamBuilder拉取当前用户的所有书签记录,拿到每条书签的文档ID后,在列表项内部监听对应question文档的变化即可。
核心修改部分为ListView.builder的itemBuilder逻辑,完整修改后的代码如下:
// 其余代码保持不变 child: ListView.builder( shrinkWrap: true, itemCount: snapshot.data!.docs.length, itemBuilder: (context, int index) { final DocumentSnapshot bookmarkDoc = snapshot.data!.docs[index]; // 书签文档ID与对应问题ID完全一致 final String questionId = bookmarkDoc.id; // 嵌套StreamBuilder拉取对应问题数据 return StreamBuilder<DocumentSnapshot>( stream: FirebaseFirestore.instance.collection('question').doc(questionId).snapshots(), builder: (context, questionSnapshot) { if (questionSnapshot.connectionState == ConnectionState.waiting) { return const Padding( padding: EdgeInsets.symmetric(vertical: 16), child: CupertinoActivityIndicator(), ); } if (questionSnapshot.hasError || !questionSnapshot.hasData || !questionSnapshot.data!.exists) { // 对应问题已删除,可按需展示占位或者自动清理无效书签 return const SizedBox.shrink(); } final questionData = questionSnapshot.data!.data() as Map<String, dynamic>; // 按需渲染问题字段,替换为你question集合的实际字段名 return ListTile( title: Text(questionData['title']), subtitle: Text(questionData['content']), onTap: () => Navigator.push( context, MaterialPageRoute(builder: (context) => DetailsPage(questionData: questionData)), ), ); }, ); }, )
方案2:批量关联查询(性能更优,适合书签数量多的场景)
嵌套StreamBuilder会为每个列表项发起独立请求,书签数量较多时性能较差,你可以提前对数据做关联转换,仅用两次请求即可拿到所有关联的问题数据,无需嵌套。
完整实现代码如下:
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:devcom/screens/detailsPage.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class Bookmark extends StatefulWidget { final User user; const Bookmark({Key? key, required this.user}) : super(key: key); @override State<Bookmark> createState() => _BookmarkState(); } class _BookmarkState extends State<Bookmark> { late User _currentUser; late final Stream<List<DocumentSnapshot>> _questionStream; @override void initState() { _currentUser = widget.user; // 对流做转换,先拉书签ID再批量查对应问题 _questionStream = FirebaseFirestore.instance .collection('bookmark') .where("uid", isEqualTo: _currentUser.uid) .snapshots() .asyncMap((bookmarkSnapshot) async { final questionIds = bookmarkSnapshot.docs.map((doc) => doc.id).toList(); if (questionIds.isEmpty) return <DocumentSnapshot>[]; // 注意:whereIn单次最多支持30个ID,超过30个需要拆分分批查询 final questionSnapshot = await FirebaseFirestore.instance .collection('question') .where(FieldPath.documentId, whereIn: questionIds) .get(); return questionSnapshot.docs; }); super.initState(); } @override Widget build(BuildContext context) { return StreamBuilder<List<DocumentSnapshot>>( stream: _questionStream, builder: (BuildContext context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) { if (snapshot.hasError) { return const Text('Something went wrong'); } if (!snapshot.hasData) { return Text('Howdy ${_currentUser.displayName}!!, 404 clever: you havent posted in a while'); } if (snapshot.connectionState == ConnectionState.waiting) { return const Text("Loading"); } final questionList = snapshot.data!; return Scaffold( appBar: AppBar( title: const Text('My Questions'), leading: IconButton( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.arrow_back_ios_new_rounded), ), ), backgroundColor: Colors.white, body: Padding( padding: const EdgeInsets.all(40), child: ListView.builder( shrinkWrap: true, itemCount: questionList.length, itemBuilder: (context, int index) { final questionData = questionList[index].data() as Map<String, dynamic>; return ListTile( title: Text(questionData['title']), subtitle: Text(questionData['content']), onTap: () => Navigator.push( context, MaterialPageRoute(builder: (context) => DetailsPage(questionData: questionData)), ), ); }, ), ), ); }, ); } }
注意事项
- 如果书签数量超过30,需要将ID数组拆分为多个长度为30的子数组,分别查询后合并结果
- 若对应问题被删除,可在检测到无效数据时自动删除对应的书签记录,避免脏数据
内容的提问来源于stack exchange,提问作者Mithson
相关产品推荐
相关产品推荐

