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

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());
          },
        ),
      ),
    );
  }
}

关键改动说明

  1. 实体类分层设计

    • Project:对应JSON中单个项目的所有字段,采用小驼峰命名(符合Dart代码规范),通过fromJson映射下划线命名的JSON字段
    • Meta:处理分页相关的元数据,支持null值(比如next_page可能为null)
    • ProjectResponse:作为顶层解析类,负责将projects数组转换为List<Project>,并解析meta对象
  2. 修正数据解析逻辑

    • 原代码直接用Album.fromJson解析顶层JSON,但顶层JSON没有id、name等字段,导致匹配失败
    • 新代码先解析顶层的projects和meta,再分别解析内部的数组和对象
  3. 更新UI展示

    • 将FutureBuilder的类型改为ProjectResponse
    • 用ListView.builder展示所有项目,替代原有的单个文本展示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:05:58