Flutter中配置实体类映射含数组与对象的自定义REST API JSON
Flutter API数据解析问题:正确配置实体类映射自定义JSON格式
你的问题核心是实体类结构和API返回的JSON结构不匹配:API返回的是包含projects数组和meta对象的顶层结构,而你原代码里的Album类试图直接解析顶层JSON(但顶层没有id、name等字段),所以触发了FormatException。
以下是完整的修正代码,直接替换原代码即可运行:
import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; // 封装API返回的完整结构 Future<ProjectResponse> fetchProjects() async { final response = await http.get( Uri.parse('https://api.demo.platform.infotechinc.com/appia/v3/projects'), headers: { HttpHeaders.authorizationHeader: 'Bearer 7bec66...', // 替换为你的有效Token }, ); if (response.statusCode == 200) { return ProjectResponse.fromJson(jsonDecode(response.body) as Map<String, dynamic>); } else { throw Exception('Failed to load projects'); } } // 单个项目实体类 class Project { final String id; final String name; final String description; final String noticeToProceedDate; final int businessId; final String status; final String awardedAmount; final String authorizedAmount; final String businessName; const Project({ required this.id, required this.name, required this.description, required this.noticeToProceedDate, required this.businessId, required this.status, required this.awardedAmount, required this.authorizedAmount, required this.businessName, }); factory Project.fromJson(Map<String, dynamic> json) { return Project( id: json['id'] as String, name: json['name'] as String, description: json['description'] as String, noticeToProceedDate: json['notice_to_proceed_date'] as String, businessId: json['business_id'] as int, status: json['status'] as String, awardedAmount: json['awarded_amount'] as String, authorizedAmount: json['authorized_amount'] as String, businessName: json['business_name'] as String, ); } } // 分页元数据实体类 class Meta { final int currentPage; final int? nextPage; final int? prevPage; final int totalPages; final int totalCount; const Meta({ required this.currentPage, this.nextPage, this.prevPage, required this.totalPages, required this.totalCount, }); factory Meta.fromJson(Map<String, dynamic> json) { return Meta( currentPage: json['current_page'] as int, nextPage: json['next_page'] as int?, prevPage: json['prev_page'] as int?, totalPages: json['total_pages'] as int, totalCount: json['total_count'] as int, ); } } // 整个API返回的结构 class ProjectResponse { final List<Project> projects; final Meta meta; const ProjectResponse({ required this.projects, required this.meta, }); factory ProjectResponse.fromJson(Map<String, dynamic> json) { final projectList = json['projects'] as List; return ProjectResponse( projects: projectList.map((item) => Project.fromJson(item as Map<String, dynamic>)).toList(), meta: Meta.fromJson(json['meta'] as Map<String, dynamic>), ); } } void main() => runApp(const MyApp()); class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { late Future<ProjectResponse> futureProjects; @override void initState() { super.initState(); futureProjects = fetchProjects(); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Fetch Projects Example', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), ), home: Scaffold( appBar: AppBar( title: const Text('Projects List'), ), body: FutureBuilder<ProjectResponse>( future: futureProjects, builder: (context, snapshot) { if (snapshot.hasData) { final projects = snapshot.data!.projects; return ListView.builder( itemCount: projects.length, itemBuilder: (context, index) { final project = projects[index]; return ListTile( title: Text(project.name), subtitle: Text('Status: ${project.status} | Amount: \$${project.awardedAmount}'), leading: Text(project.id.substring(0, 8)), // 显示ID的前8位 ); }, ); } else if (snapshot.hasError) { return Center(child: Text('${snapshot.error}')); } return const Center(child: CircularProgressIndicator()); }, ), ), ); } }
关键改动说明
实体类分层设计
Project:对应JSON中单个项目的所有字段,采用小驼峰命名(符合Dart代码规范),通过fromJson映射下划线命名的JSON字段Meta:处理分页相关的元数据,支持null值(比如next_page可能为null)ProjectResponse:作为顶层解析类,负责将projects数组转换为List<Project>,并解析meta对象
修正数据解析逻辑
- 原代码直接用
Album.fromJson解析顶层JSON,但顶层JSON没有id、name等字段,导致匹配失败 - 新代码先解析顶层的
projects和meta,再分别解析内部的数组和对象
- 原代码直接用
更新UI展示
- 将
FutureBuilder的类型改为ProjectResponse - 用
ListView.builder展示所有项目,替代原有的单个文本展示
- 将
内容的提问来源于stack exchange,提问作者JBeesky
相关产品推荐
相关产品推荐

