如何将Firebase数据以简单数组返回?Flutter查询问题求助
问题描述
我在Firebase中有两个集合:
- group
- group users
group users集合的每个文档包含「Group id」和「User id」字段;group集合的每个文档包含「Group manager id」和「Group name」字段。
需求:通过当前登录用户,先查询group users集合获取对应的Group id,再查询group集合获取对应的Group name。
但我编写的printData函数始终抛出Bad state: No element错误,即使改为异步、调整数据读取方式(如使用element.data())后问题依然存在。以下是相关代码及报错详情:
初始函数代码
printData() { var arr = []; groupUser.snapshots().listen((data) { data.docs.forEach((element) { print(element['Group id']); arr.add(element['Group id']); }); }); return arr.first; }
报错信息
The following StateError was thrown building TestView(dirty, state: _TestViewState#38814): Bad state: No element ...(原报错栈信息,保留技术术语)
异步尝试代码及报错
后续改为异步函数,仍抛出相同错误,调整为element.data()['Group id']后问题依旧:
printData() async { var arr = []; await groupUser.snapshots().listen((data) { data.docs.forEach((element) { arr.add(element['Group id']); }); }); print(arr.first); }
TestView页面相关代码
class TestView extends StatefulWidget { const TestView({Key? key}) : super(key: key); @override _TestViewState createState() => _TestViewState(); } class _TestViewState extends State<TestView> { late final FirebaseCloudStorage _groupUsersService; String get userId => AuthService.firebase().currentUser!.id; @override void initState() { _groupUsersService = FirebaseCloudStorage(); super.initState(); } @override Widget build(BuildContext context) { _groupUsersService.printData(); return Scaffold( appBar: AppBar( title: const Text('All users in my group'), actions: [ IconButton( onPressed: null, icon: const Icon(Icons.add), ), ], ), body: StreamBuilder( stream: _groupUsersService.getAllUsersInMyGroup(userId: userId), builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: case ConnectionState.active: if (snapshot.hasData) { final allUsers = snapshot.data as Iterable<CloudGroupUser>; return GroupUserListView( cloudUsers: allUsers, onTap: (job) { Navigator.of(context).pushNamed( newJobRoute, arguments: job, ); }, ); } else { return const CircularProgressIndicator(); } default: return const CircularProgressIndicator(); } }, ), ); } }
解决方案
问题根源
- 同步函数逻辑错误:
snapshots()返回的是数据流,listen是异步订阅操作,函数直接返回arr.first时,arr还未被填充数据,必然为空导致报错。 - 异步实现错误:
await无法直接作用于listen返回的StreamSubscription,listen是订阅流而非等待单次数据返回,因此arr仍可能为空就执行arr.first。 - 缺少查询条件:未给
groupUser添加过滤当前登录用户的查询条件,可能导致查询结果为空或数据不符合预期。
正确实现方式
方式1:单次查询获取数据(适合无需实时更新的场景)
Future<String?> getCurrentUserGroupName(String userId) async { // 查询当前用户对应的group users文档 final userGroupsSnapshot = await FirebaseFirestore.instance .collection('group users') .where('User id', isEqualTo: userId) .get(); if (userGroupsSnapshot.docs.isEmpty) { print('当前用户未加入任何群组'); return null; } // 获取第一个群组ID final groupId = userGroupsSnapshot.docs.first['Group id']; // 查询对应group文档获取名称 final groupSnapshot = await FirebaseFirestore.instance .collection('group') .doc(groupId) .get(); if (!groupSnapshot.exists) { print('群组文档不存在'); return null; } return groupSnapshot['Group name']; }
方式2:实时监听数据流(适合需要同步群组名称变化的场景)
Stream<String?> getCurrentUserGroupNameStream(String userId) { return FirebaseFirestore.instance .collection('group users') .where('User id', isEqualTo: userId) .snapshots() .asyncMap((userGroupsSnapshot) async { if (userGroupsSnapshot.docs.isEmpty) { return null; } final groupId = userGroupsSnapshot.docs.first['Group id']; final groupSnapshot = await FirebaseFirestore.instance.collection('group').doc(groupId).get(); return groupSnapshot.exists ? groupSnapshot['Group name'] : null; }); }
在TestView中调用示例
- 单次查询方式:在
initState或按钮点击事件中调用
@override void initState() { super.initState(); _fetchGroupName(); } void _fetchGroupName() async { final groupName = await _groupUsersService.getCurrentUserGroupName(userId); print('当前用户所属群组:$groupName'); }
- 实时监听方式:用
StreamBuilder构建UI
// 在build方法的合适位置添加 StreamBuilder<String?>( stream: _groupUsersService.getCurrentUserGroupNameStream(userId), builder: (context, snapshot) { if (snapshot.hasData) { return Text('所属群组:${snapshot.data}'); } else if (snapshot.hasError) { return Text('加载出错:${snapshot.error}'); } return const CircularProgressIndicator(); }, ),
关键注意事项
- 必须添加用户ID的查询条件,确保只获取当前登录用户的群组关联数据。
- 处理空数据场景:用户可能未加入任何群组,或群组文档已被删除,需通过判断
snapshot.docs.isEmpty和groupSnapshot.exists避免异常。 - 流操作需使用
asyncMap处理异步逻辑,不要在listen后直接操作空数组。
内容的提问来源于stack exchange,提问作者user17281101
相关产品推荐
相关产品推荐

