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

Flutter解析嵌套JSON生成带子菜单的ListTile组件问题

Flutter解析嵌套JSON生成带子菜单的ListTile组件问题

看起来你已经搭好了主菜单的基础框架,核心卡壳在怎么把嵌套的subMenu数据解析并渲染成ExpansionTile的子ListTile对吧?另外你问有没有更简单的方法或可用的包,我也会一起说明~

一、先解决当前代码的核心问题:解析subMenu并生成子ListTile

你的代码里已经定义了SubMenuItem类,但还没把JSON里的subMenu数据转成这个类的实例,而且MenuItem里的subMenu字段类型是List,最好改成List<SubMenuItem>更规范。我们一步步改:

1. 完善数据模型类

先给MenuItem和SubMenuItem添加工厂方法,方便从JSON数据转换:

class MenuItem {
  final String icon;
  final String menu;
  final String subtitle;
  final List<SubMenuItem> subMenu;

  MenuItem(this.icon, this.menu, this.subtitle, this.subMenu);

  // 从JSON转成MenuItem的工厂方法
  factory MenuItem.fromJson(Map<String, dynamic> json) {
    // 把subMenu的JSON数组转成SubMenuItem列表
    List<SubMenuItem> subMenuList = [];
    if (json['subMenu'] != null) {
      subMenuList = (json['subMenu'] as List)
          .map((subJson) => SubMenuItem.fromJson(subJson))
          .toList();
    }
    return MenuItem(
      json['icon'],
      json['menu'],
      json['subtitle'],
      subMenuList,
    );
  }
}

class SubMenuItem {
  final String icon;
  final String title;
  final String jsonFile;

  SubMenuItem(this.icon, this.title, this.jsonFile);

  // 从JSON转成SubMenuItem的工厂方法
  factory SubMenuItem.fromJson(Map<String, dynamic> json) {
    return SubMenuItem(
      json['icon'],
      json['title'],
      json['jsonFile'],
    );
  }
}

2. 简化JSON解析方法newGetCategory

原来的循环可以简化,直接用上面的工厂方法:

Future<List<MenuItem>> newGetCategory() async {
  String jsonString = await loadJsonFromAssets(menuDB);
  final jsonResponse = json.decode(jsonString);
  // 直接把每个category项转成MenuItem实例
  List<MenuItem> menuList = (jsonResponse['categories'] as List)
      .map((item) => MenuItem.fromJson(item))
      .toList();
  return menuList;
}

这里给方法指定返回类型Future<List<MenuItem>>,能让FutureBuilder的snapshot.data类型更明确,避免类型警告。

3. 创建子菜单展示组件DisplaySubMenuList

这个组件接收List<SubMenuItem>,生成对应的ListTile:

class DisplaySubMenuList extends StatelessWidget {
  final List<SubMenuItem> subMenuList;
  const DisplaySubMenuList({super.key, required this.subMenuList});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: subMenuList.map((subItem) {
        return ListTile(
          leading: Image.asset(subItem.icon), // 本地图标示例,可根据实际情况调整
          title: Text(subItem.title),
          onTap: () {
            // 处理子菜单点击事件,比如跳转或加载对应jsonFile
            print('点击子菜单:${subItem.title},对应文件:${subItem.jsonFile}');
          },
        );
      }).toList(),
    );
  }
}

4. 修改DisplayMenuList组件,渲染子菜单

把ExpansionTile的children替换成我们的子菜单组件:

class DisplayMenuList extends StatelessWidget {
  final MenuItem aMenuItem;
  const DisplayMenuList({super.key, required this.aMenuItem});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ExpansionTile(
        leading: Image.asset(aMenuItem.icon), // 主菜单图标,可按需调整
        title: Text(aMenuItem.menu),
        subtitle: Text(aMenuItem.subtitle),
        // 替换成子菜单组件
        children: [
          DisplaySubMenuList(subMenuList: aMenuItem.subMenu),
        ],
      ),
    );
  }
}

5. 完善FutureBuilder的类型提示

在_ScreenCategoryState里,把late Future newGeneratedCategory;改成明确的类型:

late Future<List<MenuItem>> newGeneratedCategory;

二、更简单的解析方法:使用json_serializable包

如果你的JSON结构后续会更复杂,手动写fromJson会很繁琐,推荐使用官方生态的json_serializable包,它能自动生成JSON序列化/反序列化代码:

1. 添加依赖到pubspec.yaml

dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.4
  json_serializable: ^6.7.1

2. 给模型类添加注解

import 'package:json_annotation/json_annotation.dart';

part 'menu_item.g.dart';

@JsonSerializable()
class MenuItem {
  final String icon;
  final String menu;
  final String subtitle;
  final List<SubMenuItem> subMenu;

  MenuItem(this.icon, this.menu, this.subtitle, this.subMenu);

  factory MenuItem.fromJson(Map<String, dynamic> json) => _$MenuItemFromJson(json);
  Map<String, dynamic> toJson() => _$MenuItemToJson(this);
}

@JsonSerializable()
class SubMenuItem {
  final String icon;
  final String title;
  final String jsonFile;

  SubMenuItem(this.icon, this.title, this.jsonFile);

  factory SubMenuItem.fromJson(Map<String, dynamic> json) => _$SubMenuItemFromJson(json);
  Map<String, dynamic> toJson() => _$SubMenuItemToJson(this);
}

3. 运行命令生成代码

flutter pub run build_runner build

之后就可以直接用MenuItem.fromJson解析,完全不用手动写转换逻辑,非常适合复杂嵌套JSON。

最后测试

改完之后运行代码,就能看到主菜单展开后显示对应的子菜单ListTile,点击子菜单也会触发你设置的逻辑~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 14:00:31