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

如何将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();
          }
        },
      ),
    );
  }
}

解决方案

问题根源

  1. 同步函数逻辑错误:snapshots()返回的是数据流,listen是异步订阅操作,函数直接返回arr.first时,arr还未被填充数据,必然为空导致报错。
  2. 异步实现错误:await无法直接作用于listen返回的StreamSubscription,listen是订阅流而非等待单次数据返回,因此arr仍可能为空就执行arr.first。
  3. 缺少查询条件:未给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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:11:51