Flutter中JSON嵌套列表数据解析问题求助
Flutter嵌套JSON列表数据渲染解决方案
问题说明
解析JSON文件时,name等基础字段可正常读取,但无法读取appDetails这类嵌套列表及其内部子JSON结构,需在现有DetailScreen页面基础上修改,实现根据用户选择的类别展示对应详情。
解决方案
你的ProductModel、AppDetails、Label模型类的JSON解析逻辑是正确的,问题出在DetailScreen的UI层没有对嵌套的appDetails数据进行渲染。以下是修改后的完整代码,可完整展示产品基础信息及嵌套详情数据:
class DetailScreen extends StatefulWidget { final ProductModel item; const DetailScreen({Key? key, required this.item}) : super(key: key); @override State<DetailScreen> createState() => _DetailScreenState(); } class _DetailScreenState extends State<DetailScreen> { @override Widget build(BuildContext context) { // 从appDetails数组中取第一个详情项(匹配你的JSON结构) final AppDetails? appDetail = widget.item.appDetails?.isNotEmpty == true ? widget.item.appDetails![0] : null; return Scaffold( appBar: AppBar(title: Text(widget.item.name ?? '产品详情')), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 产品基础信息区域 Row( children: [ Image.network( '$iconLink${widget.item.logo}', height: 107, width: 107, fit: BoxFit.contain, ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.item.name ?? '未知产品', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text('分类: ${widget.item.category ?? '未分类'}'), Text('子分类: ${widget.item.subcategory ?? '无子分类'}'), const SizedBox(height: 8), Text(widget.item.description ?? '无描述'), ], ), ), ], ), const SizedBox(height: 24), // 系统概述 if (appDetail?.systemOverview != null) ...[ const Text('系统概述', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text(appDetail!.systemOverview!), const SizedBox(height: 16), ], // 多设备支持标签列表 if (appDetail?.multiDeviceSupport?.isNotEmpty == true) ...[ const Text('多设备支持', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: appDetail!.multiDeviceSupport!.map((label) { return Chip(label: Text(label.label ?? '')); }).toList(), ), const SizedBox(height: 16), ], // 主要功能列表 if (appDetail?.mainFeatures?.isNotEmpty == true) ...[ const Text('主要功能', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Column( children: appDetail!.mainFeatures!.map((feature) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ const Icon(Icons.check_circle, color: Colors.green, size: 16), const SizedBox(width: 8), Text(feature.label ?? ''), ], ), ); }).toList(), ), const SizedBox(height: 16), ], // 产品优势列表 if (appDetail?.benefits?.isNotEmpty == true) ...[ const Text('产品优势', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Column( children: appDetail!.benefits!.map((benefit) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ const Icon(Icons.star, color: Colors.orange, size: 16), const SizedBox(width: 8), Text(benefit.label ?? ''), ], ), ); }).toList(), ), ], ], ), ), ); } } // 以下为原Model类,无需修改 class ProductModel { String? name; String? category; String? subcategory; String? imageUrl; String? logo; String? description; List<AppDetails>? appDetails; ProductModel( {this.name, this.category, this.subcategory, this.imageUrl, this.logo, this.description, this.appDetails}); ProductModel.fromJson(Map<String, dynamic> json) { name = json['name']; category = json['category']; subcategory = json['subcategory']; imageUrl = json['imageUrl']; logo = json['logo']; description = json['description']; if (json['appDetails'] != null) { appDetails = <AppDetails>[]; json['appDetails'].forEach((v) { appDetails!.add(AppDetails.fromJson(v)); }); } } Map<String, dynamic> toJson() { final Map<String, dynamic> data = <String, dynamic>{}; data['name'] = name; data['category'] = category; data['subcategory'] = subcategory; data['imageUrl'] = imageUrl; data['logo'] = logo; data['description'] = description; if (appDetails != null) { data['appDetails'] = appDetails!.map((v) => v.toJson()).toList(); } return data; } } class AppDetails { String? systemOverview; List<Label>? multiDeviceSupport; List<Label>? mainFeatures; List<Label>? benefits; AppDetails( {this.systemOverview, this.multiDeviceSupport, this.mainFeatures, this.benefits}); AppDetails.fromJson(Map<String, dynamic> json) { systemOverview = json['systemOverview']; if (json['multiDeviceSupport'] != null) { multiDeviceSupport = <Label>[]; json['multiDeviceSupport'].forEach((v) { multiDeviceSupport!.add(Label.fromJson(v)); }); } if (json['mainFeatures'] != null) { mainFeatures = <Label>[]; json['mainFeatures'].forEach((v) { mainFeatures!.add(Label.fromJson(v)); }); } if (json['benefits'] != null) { benefits = <Label>[]; json['benefits'].forEach((v) { benefits!.add(Label.fromJson(v)); }); } } Map<String, dynamic> toJson() { final Map<String, dynamic> data = <String, dynamic>{}; data['systemOverview'] = systemOverview; if (multiDeviceSupport != null) { data['multiDeviceSupport'] = multiDeviceSupport!.map((v) => v.toJson()).toList(); } if (mainFeatures != null) { data['mainFeatures'] = mainFeatures!.map((v) => v.toJson()).toList(); } if (benefits != null) { data['benefits'] = benefits!.map((v) => v.toJson()).toList(); } return data; } } class Label { String? label; Label({this.label}); Label.fromJson(Map<String, dynamic> json) { label = json['label']; } Map<String, dynamic> toJson() { final Map<String, dynamic> data = <String, dynamic>{}; data['label'] = label; return data; } }
关键修改点
- 加入
SingleChildScrollView确保内容超出屏幕时可滚动 - 增加空安全判断,避免空指针异常
- 针对不同嵌套数据类型采用适配的UI组件:
- 系统概述用普通文本展示
- 多设备支持用
Wrap+Chip实现标签式布局 - 主要功能和产品优势用带图标的列表展示
- 优化基础信息区域的布局结构,提升可读性
内容的提问来源于stack exchange,提问作者Talal Habib
相关产品推荐
相关产品推荐

