Flutter 无法调用嵌套 json API
Flutter cannot call nested json API
刚接触flutter开发,最近一直在尝试调用API。我设法做到了这一点,但没有将数据放入模型中,而且效果很好。问题是当我尝试序列化数据时。我使用 app.quicktype.io 创建了一个模型。我不确定如何在我的视图文件中调用它。
该项目正在使用 Getx。
API 经理
import 'package:api_test_2/models/event.dart';
import 'package:http/http.dart' as http;
class ApiManager {
static var client = http.Client();
// Future as <List<Event>> maybe ?
static Future<Event?> fetchEvents() async {
var response = await client.get(Uri.parse(
'https://xposure.ae/wp-json/wp/auditorium/v1/events'));
if (response.statusCode == 200) {
var jsonString = response.body;
print(jsonString);
return eventFromJson(jsonString);
} else {
//show error message
return null;
}
}
}
事件模型
// To parse this JSON data, do
//
// final event = eventFromJson(jsonString);
import 'package:meta/meta.dart';
import 'dart:convert';
Event eventFromJson(String str) => Event.fromJson(json.decode(str));
String eventToJson(Event data) => json.encode(data.toJson());
class Event {
Event({
required this.data,
});
List<Datum>? data;
factory Event.fromJson(Map<String, dynamic> json) => Event(
data: json["data"] == null ? null : List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
//null check added after data
Map<String, dynamic> toJson() => {
"data": data == null ? null : List<dynamic>.from(data!.map((x) => x.toJson())),
};
}
class Datum {
Datum({
required this.eventtitle,
required this.description,
required this.eventImage,
required this.speaker,
required this.datetime,
required this.location,
});
String? eventtitle;
String description;
String? eventImage;
Speaker? speaker;
String? datetime;
Location? location;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
eventtitle: json["Eventtitle"] == null ? null : json["Eventtitle"],
description: json["Description"] == null ? null : json["Description"],
eventImage: json["event_image"] == null ? null : json["event_image"],
speaker: json["Speaker"] == null ? null : Speaker.fromJson(json["Speaker"]),
datetime: json["datetime"] == null ? null : json["datetime"],
location: json["Location"] == null ? null : Location.fromJson(json["Location"]),
);
Map<String, dynamic> toJson() => {
"Eventtitle": eventtitle == null ? null : eventtitle,
"Description": description == null ? null : description,
"event_image": eventImage == null ? null : eventImage,
"Speaker": speaker == null ? null : speaker!.toJson(),
"datetime": datetime == null ? null : datetime,
"Location": location == null ? null : location!.toJson(),
};
}
class Location {
Location({
required this.venue,
required this.address,
});
Venue? venue;
Address? address;
factory Location.fromJson(Map<String, dynamic> json) => Location(
venue: json["venue"] == null ? null : venueValues.map[json["venue"]],
address: json["address"] == null ? null : addressValues.map[json["address"]],
);
//added null checks after reverse
Map<String, dynamic> toJson() => {
"venue": venue == null ? null : venueValues.reverse![venue],
"address": address == null ? null : addressValues.reverse![address],
};
}
enum Address { SHARJAH_BR_SHARJAH_BR_61110_BR_UNITED_ARAB_EMIRATES }
final addressValues = EnumValues({
"Sharjah</br>Sharjah,</br>61110,</br>United Arab Emirates": Address.SHARJAH_BR_SHARJAH_BR_61110_BR_UNITED_ARAB_EMIRATES
});
enum Venue { XPOSURE_INTERNATIONAL_PHOTOGRAPHY_FESTIVAL }
final venueValues = EnumValues({
"Xposure International Photography Festival": Venue.XPOSURE_INTERNATIONAL_PHOTOGRAPHY_FESTIVAL
});
class Speaker {
Speaker({
required this.speakername,
required this.link,
});
String speakername;
String link;
factory Speaker.fromJson(Map<String, dynamic> json) => Speaker(
speakername: json["speakername"] == null ? null : json["speakername"],
link: json["link"] == null ? null : json["link"],
);
Map<String, dynamic> toJson() => {
"speakername": speakername == null ? null : speakername,
"link": link == null ? null : link,
};
}
class EnumValues<T> {
Map<String, T> map;
Map<T, String>? reverseMap;
EnumValues(this.map);
Map<T, String>? get reverse {
if (reverseMap == null) {
reverseMap = map.map((k, v) => new MapEntry(v, k));
}
return reverseMap;
}
}
控制器
import 'package:api_test_2/models/event.dart';
import 'package:api_test_2/services/api_manager.dart';
import 'package:get/state_manager.dart';
class EventController extends GetxController {
var isLoading = true.obs;
// var eventList = List<Event>().obs;
var eventList = <Event>[].obs;
@override
void onInit() {
fetchEvents();
super.onInit();
}
void fetchEvents() async {
try {
isLoading(true);
var events = await ApiManager.fetchEvents();
if (events != null) {
// added cast as List<Event>
eventList.value = events as List<Event>;
}
} finally {
isLoading(false);
}
}
}
主视图
import 'package:api_test_2/controllers/event_controller.dart';
import 'package:api_test_2/views/event_tile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart';
import 'package:get/instance_manager.dart';
class HomePage extends StatelessWidget {
final EventController eventController = Get.put(EventController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
leading: const Icon(
Icons.arrow_back_ios,
),
actions: [
IconButton(
icon: const Icon(
Icons.shopping_cart,
),
onPressed: () {},
)
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Expanded(
child: Text(
'ShopX',
style: TextStyle(
fontFamily: 'avenir',
fontSize: 32,
fontWeight: FontWeight.w900),
),
),
IconButton(
icon: const Icon(Icons.view_list_rounded), onPressed: () {}),
IconButton(icon: const Icon(Icons.grid_view), onPressed: () {}),
],
),
),
Expanded(
child: Obx(() {
if (eventController.isLoading.value)
return Center(child: CircularProgressIndicator());
else {
return StaggeredGridView.countBuilder(
crossAxisCount: 1,
itemCount: eventController.eventList.length,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
itemBuilder: (context, index) {
return EventTile(eventController.eventList[index]);
},
staggeredTileBuilder: (index) => StaggeredTile.fit(1),
);
}
}),
)
],
),
);
}
}
事件板块
import 'package:api_test_2/models/event.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class EventTile extends StatelessWidget {
final Event event;
const EventTile(this.event);
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// ),
],
),
),
);
}
}
我认为我在转换为 List 或 Map 时做错了,但我不确定它是在我的 API 管理器 class 中还是在事件模型本身中。如果有人能提供帮助,我将不胜感激。
您的实际事件列表在模型的 data
属性 中。
因此,您的控制器应该是:
class EventController extends GetxController {
final isLoading = true.obs;
final event = Rxn<Event>();
@override
void onInit() async{
await fetchEvents();
super.onInit();
}
void fetchEvents() async {
try {
isLoading(true);
var response = await ApiManager.fetchEvents();
if (response != null) {
event.value = response;
}
} finally {
isLoading(false);
}
}
}
还有你的StaggeredGridView
:
return StaggeredGridView.countBuilder(
crossAxisCount: 1,
itemCount: eventController.event.value.data.length,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
itemBuilder: (context, index) {
return EventTile(eventController.event.value.data[index]);
},
staggeredTileBuilder: (index) => StaggeredTile.fit(1),
);
最后,你的 EventTile
:
class EventTile extends StatelessWidget {
final Datum datum;
const EventTile(this.datum);
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// ),
],
),
),
);
}
}
刚接触flutter开发,最近一直在尝试调用API。我设法做到了这一点,但没有将数据放入模型中,而且效果很好。问题是当我尝试序列化数据时。我使用 app.quicktype.io 创建了一个模型。我不确定如何在我的视图文件中调用它。
该项目正在使用 Getx。
API 经理
import 'package:api_test_2/models/event.dart';
import 'package:http/http.dart' as http;
class ApiManager {
static var client = http.Client();
// Future as <List<Event>> maybe ?
static Future<Event?> fetchEvents() async {
var response = await client.get(Uri.parse(
'https://xposure.ae/wp-json/wp/auditorium/v1/events'));
if (response.statusCode == 200) {
var jsonString = response.body;
print(jsonString);
return eventFromJson(jsonString);
} else {
//show error message
return null;
}
}
}
事件模型
// To parse this JSON data, do
//
// final event = eventFromJson(jsonString);
import 'package:meta/meta.dart';
import 'dart:convert';
Event eventFromJson(String str) => Event.fromJson(json.decode(str));
String eventToJson(Event data) => json.encode(data.toJson());
class Event {
Event({
required this.data,
});
List<Datum>? data;
factory Event.fromJson(Map<String, dynamic> json) => Event(
data: json["data"] == null ? null : List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
//null check added after data
Map<String, dynamic> toJson() => {
"data": data == null ? null : List<dynamic>.from(data!.map((x) => x.toJson())),
};
}
class Datum {
Datum({
required this.eventtitle,
required this.description,
required this.eventImage,
required this.speaker,
required this.datetime,
required this.location,
});
String? eventtitle;
String description;
String? eventImage;
Speaker? speaker;
String? datetime;
Location? location;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
eventtitle: json["Eventtitle"] == null ? null : json["Eventtitle"],
description: json["Description"] == null ? null : json["Description"],
eventImage: json["event_image"] == null ? null : json["event_image"],
speaker: json["Speaker"] == null ? null : Speaker.fromJson(json["Speaker"]),
datetime: json["datetime"] == null ? null : json["datetime"],
location: json["Location"] == null ? null : Location.fromJson(json["Location"]),
);
Map<String, dynamic> toJson() => {
"Eventtitle": eventtitle == null ? null : eventtitle,
"Description": description == null ? null : description,
"event_image": eventImage == null ? null : eventImage,
"Speaker": speaker == null ? null : speaker!.toJson(),
"datetime": datetime == null ? null : datetime,
"Location": location == null ? null : location!.toJson(),
};
}
class Location {
Location({
required this.venue,
required this.address,
});
Venue? venue;
Address? address;
factory Location.fromJson(Map<String, dynamic> json) => Location(
venue: json["venue"] == null ? null : venueValues.map[json["venue"]],
address: json["address"] == null ? null : addressValues.map[json["address"]],
);
//added null checks after reverse
Map<String, dynamic> toJson() => {
"venue": venue == null ? null : venueValues.reverse![venue],
"address": address == null ? null : addressValues.reverse![address],
};
}
enum Address { SHARJAH_BR_SHARJAH_BR_61110_BR_UNITED_ARAB_EMIRATES }
final addressValues = EnumValues({
"Sharjah</br>Sharjah,</br>61110,</br>United Arab Emirates": Address.SHARJAH_BR_SHARJAH_BR_61110_BR_UNITED_ARAB_EMIRATES
});
enum Venue { XPOSURE_INTERNATIONAL_PHOTOGRAPHY_FESTIVAL }
final venueValues = EnumValues({
"Xposure International Photography Festival": Venue.XPOSURE_INTERNATIONAL_PHOTOGRAPHY_FESTIVAL
});
class Speaker {
Speaker({
required this.speakername,
required this.link,
});
String speakername;
String link;
factory Speaker.fromJson(Map<String, dynamic> json) => Speaker(
speakername: json["speakername"] == null ? null : json["speakername"],
link: json["link"] == null ? null : json["link"],
);
Map<String, dynamic> toJson() => {
"speakername": speakername == null ? null : speakername,
"link": link == null ? null : link,
};
}
class EnumValues<T> {
Map<String, T> map;
Map<T, String>? reverseMap;
EnumValues(this.map);
Map<T, String>? get reverse {
if (reverseMap == null) {
reverseMap = map.map((k, v) => new MapEntry(v, k));
}
return reverseMap;
}
}
控制器
import 'package:api_test_2/models/event.dart';
import 'package:api_test_2/services/api_manager.dart';
import 'package:get/state_manager.dart';
class EventController extends GetxController {
var isLoading = true.obs;
// var eventList = List<Event>().obs;
var eventList = <Event>[].obs;
@override
void onInit() {
fetchEvents();
super.onInit();
}
void fetchEvents() async {
try {
isLoading(true);
var events = await ApiManager.fetchEvents();
if (events != null) {
// added cast as List<Event>
eventList.value = events as List<Event>;
}
} finally {
isLoading(false);
}
}
}
主视图
import 'package:api_test_2/controllers/event_controller.dart';
import 'package:api_test_2/views/event_tile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart';
import 'package:get/instance_manager.dart';
class HomePage extends StatelessWidget {
final EventController eventController = Get.put(EventController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
leading: const Icon(
Icons.arrow_back_ios,
),
actions: [
IconButton(
icon: const Icon(
Icons.shopping_cart,
),
onPressed: () {},
)
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Expanded(
child: Text(
'ShopX',
style: TextStyle(
fontFamily: 'avenir',
fontSize: 32,
fontWeight: FontWeight.w900),
),
),
IconButton(
icon: const Icon(Icons.view_list_rounded), onPressed: () {}),
IconButton(icon: const Icon(Icons.grid_view), onPressed: () {}),
],
),
),
Expanded(
child: Obx(() {
if (eventController.isLoading.value)
return Center(child: CircularProgressIndicator());
else {
return StaggeredGridView.countBuilder(
crossAxisCount: 1,
itemCount: eventController.eventList.length,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
itemBuilder: (context, index) {
return EventTile(eventController.eventList[index]);
},
staggeredTileBuilder: (index) => StaggeredTile.fit(1),
);
}
}),
)
],
),
);
}
}
事件板块
import 'package:api_test_2/models/event.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class EventTile extends StatelessWidget {
final Event event;
const EventTile(this.event);
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// ),
],
),
),
);
}
}
我认为我在转换为 List 或 Map 时做错了,但我不确定它是在我的 API 管理器 class 中还是在事件模型本身中。如果有人能提供帮助,我将不胜感激。
您的实际事件列表在模型的 data
属性 中。
因此,您的控制器应该是:
class EventController extends GetxController {
final isLoading = true.obs;
final event = Rxn<Event>();
@override
void onInit() async{
await fetchEvents();
super.onInit();
}
void fetchEvents() async {
try {
isLoading(true);
var response = await ApiManager.fetchEvents();
if (response != null) {
event.value = response;
}
} finally {
isLoading(false);
}
}
}
还有你的StaggeredGridView
:
return StaggeredGridView.countBuilder(
crossAxisCount: 1,
itemCount: eventController.event.value.data.length,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
itemBuilder: (context, index) {
return EventTile(eventController.event.value.data[index]);
},
staggeredTileBuilder: (index) => StaggeredTile.fit(1),
);
最后,你的 EventTile
:
class EventTile extends StatelessWidget {
final Datum datum;
const EventTile(this.datum);
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// ),
],
),
),
);
}
}