I am still new to flutter development and recently I've been trying to call API. I've managed to do so without placing data into the model and it worked fine. The issue is when I try to serialize the data. I've created a model using app.quicktype.io. and I am not sure how to call it in my views file.
The project is using Getx.
API manager
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;
}
}
}
Event model
// 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;
}
}
Controller
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);
}
}
}
Main view
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),
);
}
}),
)
],
),
);
}
}
Event tile
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(
// ),
],
),
),
);
}
}
I think I'm doing something wrong with the conversion to List or Map but I am not sure if it's in my API manager class or the Event model itself. If anyone could help I would be so gratefull.
CodePudding user response:
Your actual event list is inside the data
property of your model.
Therefore, your controller should be:
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);
}
}
}
And your 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),
);
And finally, your 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(
// ),
],
),
),
);
}
}