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

Flutter中如何正确访问服务器响应的嵌套动态元素?

多层嵌套数据动态索引访问失败的解决方法

问题描述

从服务器获取多层嵌套响应数据后,无法通过动态索引访问第二层嵌套元素。例如:

Text('Child header name - ${catalogDrawer[index].one[index].name}'),

这段代码无法正常运行,但将索引替换为固定数字(如0)就可以正常工作:

Text('Child header name - ${catalogDrawer[index].one[0].name}'),

同时第一层数据可以正常展示:

Text('Header name - ${catalogDrawer[index].name}'),

需要解决深层嵌套数据的动态索引访问问题。

服务器响应数据

"categories": [
        {
            "name": "Dlya nego",
            "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nego/",
            "children": [
                {
                    "name": "obyv",
                    "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nego/obuv/",
                    "children": [
                        {
                            "name": "Krossovki",
                            "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nego/obuv/krossovki/",
                            "children": []
                        },
                        {
                            "name": "slancy",
                            "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nego/obuv/slancy/",
                            "children": []
                        }
                    ]
                },
                {
                    "name": "Kurtki",
                    "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nego/Kurtki/",
                    "children": [
                        {
                            "name": "bombery",
                            "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nego/Kurtki/bombery/",
                            "children": []
                        }
                    ]
                }
            ]
        },
        {
            "name": "dlya nee",
            "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nee/",
            "children": [
                {
                    "name": "obyv",
                    "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nee/obuv/",
                    "children": [
                        {
                            "name": "Krossovki",
                            "url": "https://dev.radiwear.com/api/v1/m/catalog/dlya-nee/obuv/krossovki/",
                            "children": []
                        }
                    ]
                }
            ]
        },
        {
            "name": "deti",
            "url": "https://dev.radiwear.com/api/v1/m/catalog/deti/",
            "children": [
                {
                    "name": "obuv",
                    "url": "https://dev.radiwear.com/api/v1/m/catalog/deti/obuv/",
                    "children": [
                        {
                            "name": "krossovki",
                            "url": "https://dev.radiwear.com/api/v1/m/catalog/deti/obuv/krossovki/",
                            "children": []
                        }
                    ]
                }
            ]
        },
        {
            "name": "Sale",
            "url": "https://dev.radiwear.com/api/v1/m/catalog/sale/",
            "children": [
                {
                    "name": "obuv",
                    "url": "https://dev.radiwear.com/api/v1/m/catalog/sale/obuv/",
                    "children": [
                        {
                            "name": "Krossovki",
                            "url": "https://dev.radiwear.com/api/v1/m/catalog/sale/obuv/krossovki/",
                            "children": []
                        }
                    ]
                }
            ]
        }
    ],

实体类代码

class CatalogDrawerEntity extends Equatable {
  final String name;
  final String url;
  final List<OneChildren> one;
  const CatalogDrawerEntity ({
    required this.name,
    required this.url,
    required this.one
});
  @override
  List<Object?> get props => [name, url, one];
}

class OneChildren {
 final String name;
 final String url;
 final List<TwoChildren> two;

 const OneChildren({
   required this.name,
   required this.url,
   required this.two
});
}

class TwoChildren {
  final String name;
  final String url;
  TwoChildren({
    required this.name,
    required this.url
});
}

模型类代码

class CatalogDrawerModel extends CatalogDrawerEntity {
  const CatalogDrawerModel({
    required name,
    required url,
    required one
}) : super (
    name: name,
    url: url,
    one: one
  );

  factory CatalogDrawerModel.fromJson(Map<String, dynamic> json) {
    return CatalogDrawerModel(
        name: json['name'],
        url: json['url'],
        one: json['children'] != null
        ? (json['children'] as List<dynamic>).map((json) => OneModel.fromJson(json)).toList()
            : null
    );
  }
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'url': url,
      'children': one
    };
  }
}
class OneModel extends OneChildren{
  OneModel({name, url, two}) : super (name: name, url: url, two: two);

  factory OneModel.fromJson(Map<String, dynamic> json) {
    return OneModel(
      name: json['name'],
      url: json['url'],
      two: json['children'] != null
        ? (json['children'] as List<dynamic>).map((json) => TwoModel.fromJson(json)).toList()
          : null
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'url': url,
      'two': two
    };
  }
}

class TwoModel extends TwoChildren{
  TwoModel({name, url}) : super (name: name, url: url);

  factory TwoModel.fromJson(Map<String, dynamic> json) {
    return TwoModel(
      name: json['name'],
      url: json['url']
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'url': url
    };
  }
}

服务器数据处理代码

Future<List<CatalogDrawerModel>> getAllCatalogDrawer() async {
    final response = await client.get(
      Uri.parse(ConfigUrl.home),
        headers: {'Content-Type': 'applications/json'}
    );
    if(response.statusCode == 200) {
      List catalogDrawer = json.decode(response.body)['categories'];
      return catalogDrawer.map((data) => CatalogDrawerModel.fromJson(data)).toList();
    } else {
      throw ServerException();
    }
  }

问题分析与解决方法

核心问题

  1. 索引复用错误:访问第二层数据时使用了同一个index变量,catalogDrawer[index].one[index].name中,第一个index是外层分类的索引,第二个index是当前分类下子项的索引,两者范围不一致。比如外层第2个分类(index=1)的子项只有1个,此时用index=1访问子项就会越界。
  2. 空安全隐患:模型类中fromJson方法返回null给非空列表字段,实体类中one和two都是required的非空列表,但解析时如果children为null会返回null,导致运行时错误。

解决步骤

1. 修复索引使用错误

如果是嵌套列表展示,需要为子列表使用独立的索引变量:

// 外层ListView
ListView.builder(
  itemCount: catalogDrawer.length,
  itemBuilder: (context, outerIndex) {
    final parent = catalogDrawer[outerIndex];
    return Column(
      children: [
        Text('Header name - ${parent.name}'),
        // 内层ListView展示子项
        ListView.builder(
          shrinkWrap: true,
          itemCount: parent.one.length,
          itemBuilder: (context, innerIndex) {
            final child = parent.one[innerIndex];
            return Text('Child header name - ${child.name}');
          },
        ),
      ],
    );
  },
)

2. 修复空安全问题

解析时如果children为空或null,返回空列表而非null:

  • 修改CatalogDrawerModel.fromJson:
one: (json['children'] as List<dynamic>? ?? []).map((json) => OneModel.fromJson(json)).toList()
  • 修改OneModel.fromJson:
two: (json['children'] as List<dynamic>? ?? []).map((json) => TwoModel.fromJson(json)).toList()

3. 可选:添加越界判断

访问子项前先判断列表长度,避免越界:

if (outerIndex < catalogDrawer.length && innerIndex < catalogDrawer[outerIndex].one.length) {
  // 安全访问子项
}

内容的提问来源于stack exchange,提问作者Daniil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 18:05:26