Flutter Riverpod StreamProvider未等待循环完成便构建Widget问题
问题分析
你当前的代码存在两个核心问题:
- 异步操作不触发UI更新:在
then回调中添加元素到users列表时,ListView已经提前返回,此时UI不会自动刷新,导致列表始终为空。 - 无法实现串行异步执行:
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
相关产品推荐
相关产品推荐

