如何在FutureBuilder中用Future.wait从Firestore获取3个集合?报错求助
问题:使用Future.wait从Firestore拉取多个列表时的类型错误
需要在构建UI前从Firestore拉取三个列表,使用Future.wait时出现类型错误,以下是相关代码及报错信息:
1. 变量声明
// 用于存储Future的变量 Future<List<FilingData>>? filingList; Future<List<ClientData>>? clientList; Future<List<UserData>>? userList; // 用于存储获取到的数据 List<FilingData>? retrievedFilingList; List<ClientData>? retrievedClientList; List<UserData>? retrievedUserList;
2. 获取列表的函数(其他两个逻辑类似)
Future<List<FilingData>> retrieveFilings() async { QuerySnapshot<Map<String, dynamic>> snapshot = await _db.collection('filings').get(); return snapshot.docs .map((docSnapshot) => FilingData.fromDocumentSnapshot(docSnapshot)) .toList(); }
3. Future初始化代码
@override void initState() { super.initState(); _initRetrieval(); // 在此获取future,避免每次构建都创建 } // 为FutureBuilder设置future并获取数据 Future<void> _initRetrieval() async { filingList = filingService.retrieveFilings(); retrievedFilingList = await filingService.retrieveFilings(); clientList = clientService.retrieveClients(); retrievedClientList = await clientService.retrieveClients(); userList = userService.retrieveUsers(); retrievedUserList = await userService.retrieveUsers(); }
4. FutureBuilder实现
@override Widget build(BuildContext context) { return FutureBuilder( future: Future.wait([filingList, clientList, userList]), builder: (context, AsyncSnapshot<List<dynamic>> snapshot) { if (snapshot.hasData && snapshot.data!.isNotEmpty) { return Material( /* 构建UI的代码 */ ...
5. 报错信息
初始报错
Couldn't infer type parameter 'E'. Tried to infer 'Future<List<Object>>?' for 'E' which doesn't work: Return type declared as 'List<E>'used where 'Iterable<Future<_>>' is required. The type 'Future<List<Object>>?' was inferred from: Parameter 'element' declared as 'E' but argument is 'Future<List<FilingData>>?'. Parameter 'element' declared as 'E' but argument is 'Future<List<ClientData>>?'. Parameter 'element' declared as 'E' but argument is 'Future<List<UserData>>?'.
添加<List<dynamic>>到Future.wait后的新错误
The element type 'Future<List<FilingData>>?' can't be assigned to the list type 'Future<List<dynamic>>'.
尝试使用late声明变量后的问题
用late Future<List<FilingData>> filingList;声明变量时,因未初始化导致崩溃。
解决方案
核心问题分析
- 可空类型不匹配:
Future.wait要求传入非空的Future列表,但声明的filingList等是可空类型(Future<List<...>>?),导致类型推断失败。 - 重复调用API:
_initRetrieval中既给Future变量赋值,又直接await一遍获取函数,导致Firestore被调用两次,浪费资源。
修复步骤
步骤1:修正变量声明与初始化
去掉可空标记,用late延迟初始化,同时通过Future.wait一次性等待所有请求,避免重复调用:
// 改为非空Future变量,用late延迟初始化 late Future<List<FilingData>> filingList; late Future<List<ClientData>> clientList; late Future<List<UserData>> userList; // 存储数据的变量保留可空 List<FilingData>? retrievedFilingList; List<ClientData>? retrievedClientList; List<UserData>? retrievedUserList; @override void initState() { super.initState(); _initRetrieval(); } Future<void> _initRetrieval() async { // 先获取所有Future,避免重复调用API filingList = filingService.retrieveFilings(); clientList = clientService.retrieveClients(); userList = userService.retrieveUsers(); // 一次性等待所有请求完成,获取结果 final results = await Future.wait([ filingList, clientList, userList, ]); // 分配结果到对应变量 retrievedFilingList = results[0] as List<FilingData>; retrievedClientList = results[1] as List<ClientData>; retrievedUserList = results[2] as List<UserData>; }
步骤2:修正FutureBuilder的使用
确保传入Future.wait的是非空Future,同时完善状态处理:
@override Widget build(BuildContext context) { return FutureBuilder( future: Future.wait([filingList, clientList, userList]), builder: (context, AsyncSnapshot<List<dynamic>> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // 显示加载状态 return const CircularProgressIndicator(); } else if (snapshot.hasError) { // 处理错误 return Text('加载失败:${snapshot.error}'); } else if (snapshot.hasData) { // 此时retrievedFilingList等已赋值,直接构建UI return Material( // 你的UI代码 ); } else { return const SizedBox(); } }, ); }
额外优化建议
如果不需要单独保留Future变量,可以直接在FutureBuilder中调用Future.wait,代码更简洁:
@override Widget build(BuildContext context) { return FutureBuilder( future: Future.wait([ filingService.retrieveFilings(), clientService.retrieveClients(), userService.retrieveUsers(), ]), builder: (context, AsyncSnapshot<List<dynamic>> snapshot) { if (snapshot.hasData) { final retrievedFilingList = snapshot.data![0] as List<FilingData>; final retrievedClientList = snapshot.data![1] as List<ClientData>; final retrievedUserList = snapshot.data![2] as List<UserData>; // 构建UI } // 其他状态处理... }, ); }
注:这种方式每次build都会重新调用API,若需避免重复请求,建议使用之前的state存储Future方案。
内容的提问来源于stack exchange,提问作者TM Lynch
相关产品推荐
相关产品推荐

