Flutter ListView Builder遇[]运算符错误,如何展示Firestore城市数据?
解决Flutter中FutureBuilder结合ListView展示Firestore数据的类型错误
遇到的错误
最初报错:
The method '[]' can't be unconditionally invoked because the receiver can be 'null'.
添加!强制非空后,又出现:
The operator '[]' isn't defined for the type 'Object'. Try defining the operator '[]'.
原始代码
FutureBuilder( future: getCity(), builder: ((context, snapshot) { if (snapshot.hasData) { var DocData = snapshot.data; print(DocData); return ListView.builder(itemBuilder: ((context, index) { final String? title = DocData["Cities"]; return Text("testing"); })); } return Text('Loading...'); })) ], ), ), ), ); } Future getCity() async { var doc = await FirebaseFirestore.instance .collection('Listing') .doc('City') .get(); return doc.data(); }
Firestore数据库结构
- 集合:
Listing - 文档:
City - 字段:
Cities(Map类型),键为城市名称,对应值是包含房源信息的数组,每个房源是含Price和Title的Map。
打印获取到的数据
{Cities: {Islamabad: [{Price: 100000, Title: Apartment}, {Price: 200000, Title: House}], Mirpur: [{Price: 300000, Title: House}]}}
解决方案
1. 给Future函数指定明确返回类型
默认返回Future<Object?>会导致类型不明确,指定为Future<Map<String, dynamic>?>,让Dart识别返回的是可索引的Map:
Future<Map<String, dynamic>?> getCity() async { var doc = await FirebaseFirestore.instance .collection('Listing') .doc('City') .get(); return doc.data(); }
2. 安全解析嵌套数据
在FutureBuilder中先判断数据非空,再逐层解析Cities字段(注意它是Map而非字符串):
if (snapshot.hasData && snapshot.data != null) { final docData = snapshot.data!; final citiesMap = docData["Cities"] as Map<String, dynamic>; final cityNames = citiesMap.keys.toList(); // 提取城市名列表供ListView遍历
3. 适配ListView展示逻辑
设置itemCount为城市数量,在itemBuilder中获取对应城市的房源数据并展示:
return ListView.builder( itemCount: cityNames.length, itemBuilder: ((context, index) { final cityName = cityNames[index]; final listings = citiesMap[cityName] as List<dynamic>; // 用折叠面板展示城市及下属房源 return ExpansionTile( title: Text(cityName), children: listings.map((listing) { return ListTile( title: Text(listing["Title"]), subtitle: Text("价格: ${listing["Price"]}"), ); }).toList(), ); }), );
修改后的完整代码
FutureBuilder<Map<String, dynamic>?>( future: getCity(), builder: ((context, snapshot) { if (snapshot.hasData && snapshot.data != null) { final docData = snapshot.data!; final citiesMap = docData["Cities"] as Map<String, dynamic>; final cityNames = citiesMap.keys.toList(); return ListView.builder( itemCount: cityNames.length, itemBuilder: ((context, index) { final cityName = cityNames[index]; final listings = citiesMap[cityName] as List<dynamic>; return ExpansionTile( title: Text(cityName), children: listings.map((listing) { return ListTile( title: Text(listing["Title"]), subtitle: Text("价格: ${listing["Price"]}"), ); }).toList(), ); }), ); } return const Text('Loading...'); }), ) // ... Future<Map<String, dynamic>?> getCity() async { var doc = await FirebaseFirestore.instance .collection('Listing') .doc('City') .get(); return doc.data(); }
关键说明
- 指定返回类型直接解决了
Object无法使用[]的问题 - 类型转换
as明确了嵌套数据的结构,避免类型歧义 - 将城市名转为列表,适配ListView的
itemCount要求 - 用
ExpansionTile实现城市房源的折叠展示,提升界面可读性
内容的提问来源于stack exchange,提问作者Ali Punjabi
相关产品推荐
相关产品推荐

