You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 08:01:21