Flutter中如何用Data Model及GET请求处理嵌套JSON数据?
解决Flutter中FutureBuilder结合ListView.builder展示嵌套JSON数据的问题
问题分析
你的代码存在以下关键错误:
- 数据模型不匹配:生成的模型类是
Temperatures和Datum,但代码中误用了Product和Data,导致解析失败 - JSON解析逻辑错误:直接从顶层JSON的
data字段生成列表,未先解析包含result和data的顶层对象 - UI展示逻辑错误:试图从列表项中获取顶层字段
result,且未正确访问Datum类的属性
修正步骤
1. 完善数据模型的JSON解析方法
给生成的模型类添加fromJson工厂方法,确保JSON能正确转为对象,同时修正类型错误:
class Temperatures { final String result; final List<Datum> data; Temperatures({ required this.result, required this.data, }); factory Temperatures.fromJson(Map<String, dynamic> json) { return Temperatures( result: json['result'], data: List<Datum>.from(json['data'].map((x) => Datum.fromJson(x))), ); } } class Datum { final int id; final String lucasTvsPartNo; final String partImage; final String filterType; final String customerPartNo; final String model; final String modelImage; final String segment; final String descriptions; final String oemName; final String oemImage; final dynamic oemPartNo; final double listPrice; final double mrpPrice; // 原JSON中MRPPrice是浮点型,修正int为double final int masterCartonSize; Datum({ required this.id, required this.lucasTvsPartNo, required this.partImage, required this.filterType, required this.customerPartNo, required this.model, required this.modelImage, required this.segment, required this.descriptions, required this.oemName, required this.oemImage, required this.oemPartNo, required this.listPrice, required this.mrpPrice, required this.masterCartonSize, }); factory Datum.fromJson(Map<String, dynamic> json) { return Datum( id: json['Id'], lucasTvsPartNo: json['LucasTvsPartNo'], partImage: json['PartImage'], filterType: json['FilterType'], customerPartNo: json['CustomerPartNo'], model: json['Model'], modelImage: json['ModelImage'], segment: json['Segment'], descriptions: json['Descriptions'], oemName: json['OEMName'], oemImage: json['OEMImage'], oemPartNo: json['OEMPartNo'], listPrice: json['ListPrice'].toDouble(), mrpPrice: json['MRPPrice'].toDouble(), masterCartonSize: json['MasterCartonSize'], ); } }
2. 修正数据获取方法
修改getData方法,正确解析整个JSON结构,返回Temperatures对象:
Future<Temperatures> getData() async { var url = Uri.parse('http://35.154.214.176/LucasfilterAPI/api/Values/GetPricelist'); var response = await http.get(url, headers: { "Accept": "application/json", "content-type": "application/json" }); if (response.statusCode == 200) { return Temperatures.fromJson(json.decode(response.body)); } else { throw Exception('加载数据失败,状态码:${response.statusCode}'); } }
3. 修正UI展示逻辑
在FutureBuilder中正确获取data列表,展示Datum类的具体字段:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('产品列表')), body: FutureBuilder<Temperatures>( future: _futureData, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return const Center(child: CircularProgressIndicator()); case ConnectionState.done: if (snapshot.hasError) { return Center(child: Text('错误:${snapshot.error}')); } if (!snapshot.hasData) { return const Center(child: Text('无数据')); } List<Datum> products = snapshot.data!.data; return ListView.builder( padding: const EdgeInsets.all(8), itemCount: products.length, itemBuilder: (context, index) { final product = products[index]; return Card( margin: const EdgeInsets.symmetric(vertical: 4), child: ListTile( title: Text( product.model, style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('过滤器类型:${product.filterType}'), Text('Lucas零件号:${product.lucasTvsPartNo}'), Text('MRP:₹${product.mrpPrice} | 标价:₹${product.listPrice}'), ], ), leading: SizedBox( width: 50, height: 50, child: Image.network( 'http://35.154.214.176/LucasfilterAPI/${product.partImage}', fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Icon(Icons.image), ), ), ), ); }, ); default: return const Center(child: Text('加载中...')); } }, ), ); }
完整修正后的代码
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class Temperatures { final String result; final List<Datum> data; Temperatures({ required this.result, required this.data, }); factory Temperatures.fromJson(Map<String, dynamic> json) { return Temperatures( result: json['result'], data: List<Datum>.from(json['data'].map((x) => Datum.fromJson(x))), ); } } class Datum { final int id; final String lucasTvsPartNo; final String partImage; final String filterType; final String customerPartNo; final String model; final String modelImage; final String segment; final String descriptions; final String oemName; final String oemImage; final dynamic oemPartNo; final double listPrice; final double mrpPrice; final int masterCartonSize; Datum({ required this.id, required this.lucasTvsPartNo, required this.partImage, required this.filterType, required this.customerPartNo, required this.model, required this.modelImage, required this.segment, required this.descriptions, required this.oemName, required this.oemImage, required this.oemPartNo, required this.listPrice, required this.mrpPrice, required this.masterCartonSize, }); factory Datum.fromJson(Map<String, dynamic> json) { return Datum( id: json['Id'], lucasTvsPartNo: json['LucasTvsPartNo'], partImage: json['PartImage'], filterType: json['FilterType'], customerPartNo: json['CustomerPartNo'], model: json['Model'], modelImage: json['ModelImage'], segment: json['Segment'], descriptions: json['Descriptions'], oemName: json['OEMName'], oemImage: json['OEMImage'], oemPartNo: json['OEMPartNo'], listPrice: json['ListPrice'].toDouble(), mrpPrice: json['MRPPrice'].toDouble(), masterCartonSize: json['MasterCartonSize'], ); } } class Home extends StatefulWidget { const Home({super.key}); @override State<Home> createState() => _HomeState(); } class _HomeState extends State<Home> { late Future<Temperatures> _futureData; @override void initState() { super.initState(); _futureData = getData(); } Future<Temperatures> getData() async { var url = Uri.parse('http://35.154.214.176/LucasfilterAPI/api/Values/GetPricelist'); var response = await http.get(url, headers: { "Accept": "application/json", "content-type": "application/json" }); if (response.statusCode == 200) { return Temperatures.fromJson(json.decode(response.body)); } else { throw Exception('加载数据失败,状态码:${response.statusCode}'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('产品列表')), body: FutureBuilder<Temperatures>( future: _futureData, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return const Center(child: CircularProgressIndicator()); case ConnectionState.done: if (snapshot.hasError) { return Center(child: Text('错误:${snapshot.error}')); } if (!snapshot.hasData) { return const Center(child: Text('无数据')); } List<Datum> products = snapshot.data!.data; return ListView.builder( padding: const EdgeInsets.all(8), itemCount: products.length, itemBuilder: (context, index) { final product = products[index]; return Card( margin: const EdgeInsets.symmetric(vertical: 4), child: ListTile( title: Text( product.model, style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text('过滤器类型:${product.filterType}'), Text('Lucas零件号:${product.lucasTvsPartNo}'), Text('MRP:₹${product.mrpPrice} | 标价:₹${product.listPrice}'), ], ), leading: SizedBox( width: 50, height: 50, child: Image.network( 'http://35.154.214.176/LucasfilterAPI/${product.partImage}', fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Icon(Icons.image), ), ), ), ); }, ); default: return const Center(child: Text('加载中...')); } }, ), ); } }
内容的提问来源于stack exchange,提问作者Vishnu Kumar
相关产品推荐
相关产品推荐

