{
"projects": [
{
"id": "7c635c45-ebab-44fc-8141-2157e062f97d",
"name": "Project 1",
"description": "Project description",
"notice_to_proceed_date": "2024-01-01",
"business_id": 4046,
"status": "construction",
"awarded_amount": "799109.2",
"authorized_amount": "799109.2",
"business_name": "My Business Name"
},
{
"id": "8528ebbb-54db-40e2-ba0d-1a90e3dfdfb7",
"name": "Project 2",
"description": "Project description",
"notice_to_proceed_date": "2024-03-03",
"business_id": 4046,
"status": "construction",
"awarded_amount": "31166.03",
"authorized_amount": "31166.03",
"business_name": "My Business Name"
}
],
"meta": {
"current_page": 1,
"next_page": null,
"prev_page": null,
"total_pages": 1,
"total_count": 2
}
}
我的代码
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http.get(
Uri.parse('https://api.demo.platform.infotechinc.com/appia/v3/projects'),
// Send authorization headers to the backend
headers: {
HttpHeaders.authorizationHeader: 'Bearer 7bec66...'
},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final String id;
final String name;
final String description;
final String notice_to_proceed_date;
final int business_id;
final String status;
final String awarded_amount;
final String authorized_amount;
final String business_name;
const Album({
required this.id,
required this.name,
required this.description,
required this.notice_to_proceed_date,
required this.business_id,
required this.status,
required this.awarded_amount,
required this.authorized_amount,
required this.business_name,
});
factory Album.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
'id': String id,
'name': String name,
'description': String description,
'notice_to_proceed_date': String notice_to_proceed_date,
'business_id': int business_id,
'status': String status,
'awarded_amount': String awarded_amount,
'authorized_amount': String authorized_amount,
'business_name': String business_name,
} =>
Album(
id: id,
name: name,
description: description,
notice_to_proceed_date: notice_to_proceed_date,
business_id: business_id,
status: status,
awarded_amount: awarded_amount,
authorized_amount: authorized_amount,
business_name: business_name,
),
_=> throw const FormatException('Failed to load album.'),
};
}
}
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<Album> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.name);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}
link link to api文档https://infotechinc.stoplight.io/docs/appia/df3aded65f065-list-all-projects
这里是更新的代码。
{
"projects": [
{
"id": "7c635c45-ebab-44fc-8141-2157e062f97d",
"name": "Project 1",
"description": "Project description",
"notice_to_proceed_date": "2024-01-01",
"business_id": 4046,
"status": "construction",
"awarded_amount": "799109.2",
"authorized_amount": "799109.2",
"business_name": "My Business Name"
},
{
"id": "8528ebbb-54db-40e2-ba0d-1a90e3dfdfb7",
"name": "Project 2",
"description": "Project description",
"notice_to_proceed_date": "2024-03-03",
"business_id": 4046,
"status": "construction",
"awarded_amount": "31166.03",
"authorized_amount": "31166.03",
"business_name": "My Business Name"
}
],
"meta": {
"current_page": 1,
"next_page": null,
"prev_page": null,
"total_pages": 1,
"total_count": 2
}
}
模型 它被重命名以匹配预期数据
class Project {
final String? id;
final String? name;
final String? description;
final DateTime? noticeToProceedDate;
final int? businessId;
final String? status;
final String? awardedAmount;
final String? authorizedAmount;
final String? businessName;
Project({
this.id,
this.name,
this.description,
this.noticeToProceedDate,
this.businessId,
this.status,
this.awardedAmount,
this.authorizedAmount,
this.businessName,
});
Project copyWith({
String? id,
String? name,
String? description,
DateTime? noticeToProceedDate,
int? businessId,
String? status,
String? awardedAmount,
String? authorizedAmount,
String? businessName,
}) =>
Project(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
noticeToProceedDate: noticeToProceedDate ?? this.noticeToProceedDate,
businessId: businessId ?? this.businessId,
status: status ?? this.status,
awardedAmount: awardedAmount ?? this.awardedAmount,
authorizedAmount: authorizedAmount ?? this.authorizedAmount,
businessName: businessName ?? this.businessName,
);
factory Project.fromJson(Map<String, dynamic> json) => Project(
id: json["id"],
name: json["name"],
description: json["description"],
noticeToProceedDate: json["notice_to_proceed_date"] == null
? null
: DateTime.parse(json["notice_to_proceed_date"]),
businessId: json["business_id"],
status: json["status"],
awardedAmount: json["awarded_amount"],
authorizedAmount: json["authorized_amount"],
businessName: json["business_name"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"description": description,
"notice_to_proceed_date":
"${noticeToProceedDate!.year.toString().padLeft(4, '0')}-${noticeToProceedDate!.month.toString().padLeft(2, '0')}-${noticeToProceedDate!.day.toString().padLeft(2, '0')}",
"business_id": businessId,
"status": status,
"awarded_amount": awardedAmount,
"authorized_amount": authorizedAmount,
"business_name": businessName,
};
}
API呼叫的重命名还指出,项目密钥是一个列表,我们获取每个项目并将其映射到我们的项目模型
Future<List<Project>?> fetchProjects() async {
final response = await http.get(
Uri.parse('https://api.demo.platform.infotechinc.com/appia/v3/projects'),
// Send authorization headers to the backend
headers: {HttpHeaders.authorizationHeader: 'Bearer 7bec66...'},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return List<Project>.from(
((jsonDecode(response.body)?['projects'] as List?) ?? [])
.map((e) => Project.fromJson(e)));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
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<List<Project>?> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchProjects();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<List<Project>?>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Expanded(
child: ListView.builder(
itemBuilder: (_, i) {
final name = snapshot.data?[i].name;
return Text(name ?? "");
},
itemCount: snapshot.data?.length,
shrinkWrap: true,
),
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}