Flutter Web实现Firebase列表项点击跳转至详情页需求
Flutter Web实现Firestore文档点击跳转详情页(含子集合展示)
一、修改列表项,添加点击跳转逻辑
给ListTile添加onTap事件,通过Navigator跳转到详情页并传递当前文档ID(后续用来查询完整数据和子集合),修改后的列表代码如下:
StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance.collection('Guests').snapshots(), builder: (context, snapshots) { if (snapshots.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshots.hasError) { return Center(child: Text('加载失败: ${snapshots.error}')); } return ListView.builder( itemCount: snapshots.data!.docs.length, itemBuilder: (context, index) { final doc = snapshots.data!.docs[index]; final data = doc.data() as Map<String, dynamic>; return ListTile( title: Text( data['Drug Name'] ?? '无名称', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Colors.black54, fontSize: 20, fontWeight: FontWeight.bold, ), ), subtitle: Text( data['Drug Serial Number'] ?? '无编号', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Colors.black54, fontSize: 16, fontWeight: FontWeight.bold, ), ), // 点击跳转至详情页 onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => GuestDetailPage(documentId: doc.id), ), ); }, ); }, ); }, )
二、创建详情页组件
新建GuestDetailPage组件,接收文档ID后分别查询主文档数据和子集合数据,自动展示所有字段:
class GuestDetailPage extends StatelessWidget { final String documentId; const GuestDetailPage({super.key, required this.documentId}); // 获取主文档实时数据流 Stream<DocumentSnapshot> getGuestStream() { return FirebaseFirestore.instance .collection('Guests') .doc(documentId) .snapshots(); } // 获取子集合实时数据流(替换为你的子集合名称) Stream<QuerySnapshot> getSubCollectionStream() { return FirebaseFirestore.instance .collection('Guests') .doc(documentId) .collection('subCollection') .snapshots(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('详情信息')), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 展示主文档所有字段 StreamBuilder<DocumentSnapshot>( stream: getGuestStream(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Text('加载失败: ${snapshot.error}'); } final data = snapshot.data!.data() as Map<String, dynamic>; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('主文档信息', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 12), // 遍历所有字段自动展示 ...data.entries.map((entry) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('${entry.key}: ', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), Expanded( child: Text(entry.value.toString(), style: const TextStyle(fontSize: 16)), ), ], ), ); }), ], ); }, ), const SizedBox(height: 24), // 展示子集合数据 StreamBuilder<QuerySnapshot>( stream: getSubCollectionStream(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Text('子集合加载失败: ${snapshot.error}'); } final docs = snapshot.data!.docs; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('子集合信息 (共${docs.length}条)', style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 12), if (docs.isEmpty) const Text('暂无子集合数据') else ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: docs.length, itemBuilder: (context, index) { final subData = docs[index].data() as Map<String, dynamic>; return Card( margin: const EdgeInsets.symmetric(vertical: 8), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: subData.entries.map((entry) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('${entry.key}: ', style: const TextStyle(fontWeight: FontWeight.bold)), Expanded(child: Text(entry.value.toString())), ], ), ); }).toList(), ), ), ); }, ), ], ); }, ), ], ), ), ); } }
三、关键注意事项
- 替换代码中的
subCollection为你实际的子集合名称; - 确保Firestore安全规则允许Web端读取
Guests集合及其子集合; - 若不需要实时更新,可将
StreamBuilder替换为FutureBuilder,用get()方法获取一次性数据; - 代码中已对
null值做基础处理,可根据需求调整错误提示逻辑。
内容的提问来源于stack exchange,提问作者Mariam Ashraf Abdel Rahman Ahm
相关产品推荐
相关产品推荐

