Flutter遍历动态键API响应报错,需实现店铺商品列表视图
解决Flutter中API响应解析与ListView展示的问题
我来帮你一步步搞定这个API解析和UI展示的问题,咱们先把核心问题拆解,再逐个击破:
一、修正Model类的序列化问题
首先解决你提到的两个核心错误:
- 字段名映射错误:把
item字段错误映射成product,我们需要在Model里明确指定JSON字段名; - id类型不兼容:响应中Item的id既有
int(比如35)又有String(比如"jk"),所以我们可以把id类型设为dynamic,或者统一转成String类型来兼容两种情况。
这里推荐使用json_serializable包来做序列化,既规范又不容易出错,先看修正后的Model类:
import 'package:json_annotation/json_annotation.dart'; part 'cart_models.g.dart'; // 顶层响应Model @JsonSerializable() class CartResponse { final bool success; final Map<String, List<CartItem>> data; CartResponse({required this.success, required this.data}); factory CartResponse.fromJson(Map<String, dynamic> json) => _$CartResponseFromJson(json); } // 购物车条目Model @JsonSerializable() class CartItem { final int id; final int cart_id; // 修正字段映射:指定JSON字段为"item" @JsonKey(name: "item") final Item product; CartItem({required this.id, required this.cart_id, required this.product}); factory CartItem.fromJson(Map<String, dynamic> json) => _$CartItemFromJson(json); } // 商品Model @JsonSerializable() class Item { // 用dynamic兼容int和String类型的id,或者用String并在fromJson里转换 final dynamic id; final String name; Item({required this.id, required this.name}); factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json); // 如果想统一转成String,可以自定义fromJson逻辑: // factory Item.fromJson(Map<String, dynamic> json) { // return Item( // id: json['id'].toString(), // name: json['name'], // ); // } }
记得运行flutter pub run build_runner build生成对应的序列化代码。
二、解析API响应
拿到API返回的字符串后,先转成Map,再用我们的Model类解析:
import 'dart:convert'; import 'package:http/http.dart' as http; Future<CartResponse> fetchCartData() async { final response = await http.get(Uri.parse("你的API接口地址")); if (response.statusCode == 200) { final Map<String, dynamic> jsonData = jsonDecode(response.body); return CartResponse.fromJson(jsonData); } else { throw Exception("加载购物车数据失败"); } }
三、实现按店铺分组的ListView
接下来我们要实现先展示店铺名称,再在每个店铺下展示对应商品的UI。这里提供两种常用方案:
方案1:使用ExpansionTile(可折叠店铺列表)
适合需要折叠/展开店铺商品的场景:
class CartScreen extends StatefulWidget { const CartScreen({super.key}); @override State<CartScreen> createState() => _CartScreenState(); } class _CartScreenState extends State<CartScreen> { late Future<CartResponse> _cartFuture; @override void initState() { super.initState(); _cartFuture = fetchCartData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("我的购物车")), body: FutureBuilder<CartResponse>( future: _cartFuture, builder: (context, snapshot) { if (snapshot.hasData) { final cartData = snapshot.data!; return ListView.builder( itemCount: cartData.data.length, itemBuilder: (context, index) { final storeName = cartData.data.keys.elementAt(index); final items = cartData.data[storeName]!; return ExpansionTile( title: Text(storeName, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), children: [ ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: items.length, itemBuilder: (context, itemIndex) { final product = items[itemIndex].product; return ListTile( leading: Text(product.id.toString()), title: Text(product.name), subtitle: Text("购物车条目ID: ${items[itemIndex].id}"), ); }, ), ], ); }, ); } else if (snapshot.hasError) { return Center(child: Text("${snapshot.error}")); } return const Center(child: CircularProgressIndicator()); }, ), ); } }
方案2:直接平铺展示(不可折叠)
如果不需要折叠功能,可以直接用Column嵌套展示:
// 在FutureBuilder的hasData分支替换为以下代码: return ListView.builder( itemCount: cartData.data.length, itemBuilder: (context, index) { final storeName = cartData.data.keys.elementAt(index); final items = cartData.data[storeName]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(16.0), child: Text(storeName, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), ), ...items.map((item) { final product = item.product; return ListTile( leading: Text(product.id.toString()), title: Text(product.name), subtitle: Text("购物车条目ID: ${item.id}"), ); }).toList(), ], ); }, );
这样就能完美解决你的问题:既修正了API解析的两个错误,又实现了按店铺分组展示商品的ListView。
内容的提问来源于stack exchange,提问作者vellai durai
相关产品推荐
相关产品推荐

