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(); } }
问题分析与解决方法
核心问题
- 索引复用错误:访问第二层数据时使用了同一个
index变量,catalogDrawer[index].one[index].name中,第一个index是外层分类的索引,第二个index是当前分类下子项的索引,两者范围不一致。比如外层第2个分类(index=1)的子项只有1个,此时用index=1访问子项就会越界。 - 空安全隐患:模型类中
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
相关产品推荐
相关产品推荐

