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

Flutter Riverpod StreamProvider未等待循环完成便构建Widget问题

问题分析

你当前的代码存在两个核心问题:

  1. 异步操作不触发UI更新:在then回调中添加元素到users列表时,ListView已经提前返回,此时UI不会自动刷新,导致列表始终为空。
  2. 无法实现串行异步执行:for循环中直接调用getContactInfo().then()会同时发起所有异步请求,不会等待上一个请求完成再进入下一次迭代。
解决方案

下面提供两种可行的解决思路,根据你的架构需求选择:

思路1:在UI层用FutureBuilder处理串行异步

将异步获取联系人的逻辑封装为异步函数,通过FutureBuilder监听异步状态,同时用await实现串行执行:

// 封装串行获取联系人的异步函数
Future<List<EmergencyContactModel>> _fetchContacts(List conversations, EmergencyContactsController contacts, String currentUserID) async {
  List<EmergencyContactModel> users = [];
  // 使用await实现串行循环,每次等待当前请求完成再执行下一次
  for (Map<String, dynamic> user in conversations) {
    final value = await contacts.getContactInfo(uid: user['userID']);
    if (value != null) {
      EmergencyContactModel contact = EmergencyContactModel.fromJson(value);
      contact.messageID = value["id"] + currentUserID;
      users.add(contact);
    }
  }
  return users;
}

// 修改build中的when回调
_secondTab.when(
  data: (data) {
    if (!data.exists) {
      return const Text("no data");
    }
    final doc = data.doc() as Map<String, dynamic>;
    final conversations = doc['conversation'];
    final contacts = EmergencyContactsController(currentUserID: ref.read(authProvider).currentUser!.uid);
    final currentUserID = ref.read(authProvider).currentUser!.uid;

    // 用FutureBuilder监听异步结果
    return FutureBuilder<List<EmergencyContactModel>>(
      future: _fetchContacts(conversations, contacts, currentUserID),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const CircularProgressIndicator();
        } else if (snapshot.hasError) {
          return const Text("加载联系人失败");
        } else if (snapshot.data?.isEmpty ?? true) {
          return const Text("暂无联系人");
        }
        return ListView.builder(
          itemCount: snapshot.data!.length,
          itemBuilder: (context, index) => Text(snapshot.data![index].name),
        );
      },
    );
  },
  error: (err, _) => const Text("Error"),
  loading: () => const CircularProgressIndicator(),
);

思路2:将数据处理逻辑移到StreamProvider(推荐)

把串行获取联系人的逻辑整合到StreamProvider中,让Stream直接输出处理完成的联系人列表,UI层只需监听结果:

// 修改StreamProvider定义
final secondTabProvider = StreamProvider<List<EmergencyContactModel>>((ref) async* {
  final currentUserID = ref.read(authProvider).currentUser!.uid;
  final contacts = EmergencyContactsController(currentUserID: currentUserID);

  // 监听原始联系人数据流
  await for (final data in contacts.getUserEmergencyContacts()) {
    if (!data.exists) {
      yield [];
      continue;
    }
    final doc = data.doc() as Map<String, dynamic>;
    final conversations = doc['conversation'];
    final List<EmergencyContactModel> users = [];

    // 串行获取每个联系人信息
    for (final user in conversations) {
      final value = await contacts.getContactInfo(uid: user['userID']);
      if (value != null) {
        EmergencyContactModel contact = EmergencyContactModel.fromJson(value);
        contact.messageID = value["id"] + currentUserID;
        users.add(contact);
      }
    }
    yield users;
  }
});

// UI层简化调用
_secondTab.when(
  data: (users) {
    return users.isEmpty 
        ? const Text("no data") 
        : ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) => Text(users[index].name),
          );
  },
  error: (err, _) => const Text("Error"),
  loading: () => const CircularProgressIndicator(),
);
额外优化:并行获取提升效率

如果不需要严格串行执行(即联系人之间无依赖关系),可以用Future.wait并行发起所有请求,大幅提升加载速度:

// 并行获取的写法(替换串行循环部分)
final futures = conversations.map((user) => contacts.getContactInfo(uid: user['userID'])).toList();
final results = await Future.wait(futures);

for (final value in results) {
  if (value != null) {
    EmergencyContactModel contact = EmergencyContactModel.fromJson(value);
    contact.messageID = value["id"] + currentUserID;
    users.add(contact);
  }
}

内容的提问来源于stack exchange,提问作者Dre Day

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:35:17