When I start the project I got an unexpected null value.
I keep getting this error with this API, I don't have certain of what can be.
you can check here
api link: https://ffxivcollect.com/api/mounts/257
response
{
"id": 257,
"name": "Island Eggplant Knight",
"description": "Summon forth your island Eggplant Knight, an oversized vegetable embodiment of chivalry.",
"enhanced_description": "Hailing from the selfsame land as the Tomato King, the Eggplant Knight shared a crate with the sovereign when they were shipped out. However, he fell overboard when the vessel encountered rough seas, and drifted for days before being deposited on a desert island. Among other feats, he prides himself on having played a key role in the founding of the West Mandra Empire.",
"tooltip": "I thought ye were a feral turnip, a murderous eggplant, or summat like that! - Tiny Trader",
"movement": "Terrestrial",
"seats": 1,
"order": 240,
"order_group": 99,
"patch": "6.2",
"item_id": null,
"tradeable": false,
"owned": "3.9%",
"image": "https://ffxivcollect.com/images/mounts/large/257.png",
"icon": "https://ffxivcollect.com/images/mounts/small/257.png",
"bgm": "https://ffxivcollect.com/music/BGM_Ride_MJI.ogg",
"sources": [
{
"type": "Purchase",
"text": "12,000 Seafarer's Cowries",
"related_type": null,
"related_id": null
}
]
}
I'm using quick type to convert the JSON to dart and paste in the model only adding Required.
my home page:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(''),
),
body: ListView.builder(
itemCount: finalApi!.length,
itemBuilder: (context, index) {
return Container(
child: Text(finalApi![index].description),
);
},
),
);
}
}
url:
class RemoteService {
Future<List<FinalApi>> getPosts() async {
var client = http.Client();
var uri = Uri.parse('https://ffxivcollect.com/api/mounts/257');
var response = await client.get(uri);
if (response.statusCode == 200) {
var json = response.body;
return finalApiFromJson(json);
}
throw Text('aa');
}
}
my model:
List<FinalApi> finalApiFromJson(String str) =>
List<FinalApi>.from(json.decode(str).map((x) => FinalApi.fromJson(x)));
String finalApiToJson(List<FinalApi> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class FinalApi {
FinalApi({
required this.id,
required this.name,
required this.description,
required this.enhancedDescription,
required this.tooltip,
required this.movement,
required this.seats,
required this.order,
required this.orderGroup,
required this.patch,
required this.itemId,
required this.tradeable,
required this.owned,
required this.image,
required this.icon,
required this.bgm,
required this.sources,
});
int id;
String name;
String description;
String enhancedDescription;
String tooltip;
String movement;
int seats;
int order;
int orderGroup;
String patch;
dynamic itemId;
bool tradeable;
String owned;
String image;
String icon;
String bgm;
List<Source> sources;
factory FinalApi.fromJson(Map<String, dynamic> json) => FinalApi(
id: json["id"],
name: json["name"],
description: json["description"],
enhancedDescription: json["enhanced_description"],
tooltip: json["tooltip"],
movement: json["movement"],
seats: json["seats"],
order: json["order"],
orderGroup: json["order_group"],
patch: json["patch"],
itemId: json["item_id"],
tradeable: json["tradeable"],
owned: json["owned"],
image: json["image"],
icon: json["icon"],
bgm: json["bgm"],
sources:
List<Source>.from(json["sources"].map((x) => Source.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"description": description,
"enhanced_description": enhancedDescription,
"tooltip": tooltip,
"movement": movement,
"seats": seats,
"order": order,
"order_group": orderGroup,
"patch": patch,
"item_id": itemId,
"tradeable": tradeable,
"owned": owned,
"image": image,
"icon": icon,
"bgm": bgm,
"sources": List<dynamic>.from(sources.map((x) => x.toJson())),
};
}
class Source {
Source({
required this.type,
required this.text,
required this.relatedType,
required this.relatedId,
});
String type;
String text;
dynamic relatedType;
dynamic relatedId;
factory Source.fromJson(Map<String, dynamic> json) => Source(
type: json["type"],
text: json["text"],
relatedType: json["related_type"],
relatedId: json["related_id"],
);
Map<String, dynamic> toJson() => {
"type": type,
"text": text,
"related_type": relatedType,
"related_id": relatedId,
};
}
Based on your response, It is returning a single item on map. Therefor the parser will be like
final data = FinalApi.fromJson(jsonDecode(json));
class RemoteService {
Future<List<FinalApi>> getPosts() async {
var client = http.Client();
var uri = Uri.parse('https://ffxivcollect.com/api/mounts/257');
var response = await client.get(uri);
try {
if (response.statusCode == 200) {
var json = response.body;
// finalApiFromJson(json);
final data = FinalApi.fromJson(jsonDecode(json));
return [data];
}
} catch (e) {
log(e.toString());
}
return [];
}
}
Related
i have my api which returns the json data, within the json there is multiple nested parts that i want to use. i have my models setup. how do i return them back to my futurebuilder and then use them i.e.
snapshot.data['media'][0].fieldname or snapshot.data['locationData'][0].fieldname.
I have change the code back so at the moment it only returns the LocationData but not the MediaData. this is the part i need help with returning all of it at the same time to use on my page.
here is what the json looks like when i get it back from the api
{
"recordsTotal": 1,
"sensordate": "2022-01-20",
"data": [
{
"id": 2,
"locationname": "The Old Man of Storr",
"description": "",
"shortdescription":"",
"latitude": "55.9486",
"longitude": " -3.1910",
"mainimageurl": "",
"road": "",
"addressline1": "",
"addressline2": "",
"town": "",
"county": "",
"postcode": "",
"areaid": 2,
"headerimageid": 450,
"carparkcapacity": 100,
"title": "Old Town",
"originalfilename": "OldTown.jpg",
"ext": "jpg",
"alttext": "Old Town",
"savelocation": "/library/images/locations/",
"filename": "cd8d5c511f3b64758238e75c1aa7c84d"
}
],
"media": [
{
"locationid": 2,
"mediaid": 32,
"title": "Old Town History",
"systemfilename": "/library/media/64c8ba08a781ef8b8b2a08c62d0427b6.mp3",
"transcriptionnotes": "Transcription of media file",
"mediatype": "audio",
"originalfilename": "oldtown.mp3",
"locationname": "Old Town"
}
]
}
//This is my 2 models
class LocationData {
final int id;
final String locationname;
final String description;
final String shortdescription;
final double latitude;
final double longitude;
final String mainimageurl;
final String imagefilename;
final String imagelocation;
final String imageext;
final int carparkcapacity;
final int areaid;
LocationData(
{required this.id,
required this.locationname,
required this.description,
required this.carparkcapacity,
required this.areaid,
required this.imageext,
required this.imagefilename,
required this.imagelocation,
required this.latitude,
required this.longitude,
required this.mainimageurl,
required this.shortdescription,
});
factory LocationData.fromJson(json) => LocationData(
locationname: json['locationname'],
id: json['id'] == null ? 0 : json["id"],
latitude: double.parse(json['latitude']),
longitude: double.parse(json['longitude']),
areaid: json['areaid'] == null ? 0 : json["areaid"],
description: json['description'] == null
? 'Welcome to Skye'
: json["description"],
shortdescription: json['shortdescription'] == null
? 'Welcome'
: json["shortdescription"],
imageext: json['ext'] == null ? 'jpg' : json["ext"],
carparkcapacity:
json['carparkcapacity'] == null ? 0 : json["carparkcapacity"],
imagefilename:
json['filename'] == null ? 'logo' : json["filename"],
imagelocation: json['savelocation'] == null
? '/assets/imgs/'
: json["savelocation"],
mainimageurl: json['mainimageurl'] == null
? '/assets/imgs/logo.jpg'
: json["mainimageurl"],
);
}
//Similar to above
class MediaData{}
//Getting the data
class LocationDataApi {
static Future<List<LocationData>> getLocationData(
String locationName) async {
final hotspotLocationsURL =
'${config.apiUrl}/locations.php?action=get_location&location=' +
locationName;
try {
final response = await http.get(Uri.parse(hotspotLocationsURL));
if (response.statusCode == 200) {
final body = json.decode(response.body);
//Returned Data
return body['data'].map<LocationData>(LocationData.fromJson).toList();
} else {
throw 'Error Retrieving Data';
}
} catch (e) {
if (kDebugMode) {
print(e);
}
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load Location Data');
}
}
}
on my page
class _AlreadyHereState extends State<AlreadyHere> {
late Future<List<LocationData>> _locationData;
}
....other code
FutureBuilder(
future: _locationData,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.error != null) {
return const Center(child: Text('an error occured!'));
} else {
//this part how do i get the data? snapshot.data['media'][0].fieldname doesnt work/
for starters, you can change your model to look this
// To parse this JSON data, do
//
// final someClass = someClassFromJson(jsonString);
import 'package:meta/meta.dart';
import 'dart:convert';
class SomeClass {
SomeClass({
required this.recordsTotal,
required this.sensordate,
required this.data,
required this.media,
});
final int recordsTotal;
final DateTime sensordate;
final List<Datum> data;
final List<Media> media;
factory SomeClass.fromRawJson(String str) => SomeClass.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory SomeClass.fromJson(Map<String, dynamic> json) => SomeClass(
recordsTotal: json["recordsTotal"],
sensordate: DateTime.parse(json["sensordate"]),
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
media: List<Media>.from(json["media"].map((x) => Media.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"recordsTotal": recordsTotal,
"sensordate": "${sensordate.year.toString().padLeft(4, '0')}-${sensordate.month.toString().padLeft(2, '0')}-${sensordate.day.toString().padLeft(2, '0')}",
"data": List<dynamic>.from(data.map((x) => x.toJson())),
"media": List<dynamic>.from(media.map((x) => x.toJson())),
};
}
class Datum {
Datum({
required this.id,
required this.locationname,
required this.description,
required this.shortdescription,
required this.latitude,
required this.longitude,
required this.mainimageurl,
required this.road,
required this.addressline1,
required this.addressline2,
required this.town,
required this.county,
required this.postcode,
required this.areaid,
required this.headerimageid,
required this.carparkcapacity,
required this.title,
required this.originalfilename,
required this.ext,
required this.alttext,
required this.savelocation,
required this.filename,
});
final int id;
final String locationname;
final String description;
final String shortdescription;
final String latitude;
final String longitude;
final String mainimageurl;
final String road;
final String addressline1;
final String addressline2;
final String town;
final String county;
final String postcode;
final int areaid;
final int headerimageid;
final int carparkcapacity;
final String title;
final String originalfilename;
final String ext;
final String alttext;
final String savelocation;
final String filename;
factory Datum.fromRawJson(String str) => Datum.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
id: json["id"],
locationname: json["locationname"],
description: json["description"],
shortdescription: json["shortdescription"],
latitude: json["latitude"],
longitude: json["longitude"],
mainimageurl: json["mainimageurl"],
road: json["road"],
addressline1: json["addressline1"],
addressline2: json["addressline2"],
town: json["town"],
county: json["county"],
postcode: json["postcode"],
areaid: json["areaid"],
headerimageid: json["headerimageid"],
carparkcapacity: json["carparkcapacity"],
title: json["title"],
originalfilename: json["originalfilename"],
ext: json["ext"],
alttext: json["alttext"],
savelocation: json["savelocation"],
filename: json["filename"],
);
Map<String, dynamic> toJson() => {
"id": id,
"locationname": locationname,
"description": description,
"shortdescription": shortdescription,
"latitude": latitude,
"longitude": longitude,
"mainimageurl": mainimageurl,
"road": road,
"addressline1": addressline1,
"addressline2": addressline2,
"town": town,
"county": county,
"postcode": postcode,
"areaid": areaid,
"headerimageid": headerimageid,
"carparkcapacity": carparkcapacity,
"title": title,
"originalfilename": originalfilename,
"ext": ext,
"alttext": alttext,
"savelocation": savelocation,
"filename": filename,
};
}
class Media {
Media({
required this.locationid,
required this.mediaid,
required this.title,
required this.systemfilename,
required this.transcriptionnotes,
required this.mediatype,
required this.originalfilename,
required this.locationname,
});
final int locationid;
final int mediaid;
final String title;
final String systemfilename;
final String transcriptionnotes;
final String mediatype;
final String originalfilename;
final String locationname;
factory Media.fromRawJson(String str) => Media.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Media.fromJson(Map<String, dynamic> json) => Media(
locationid: json["locationid"],
mediaid: json["mediaid"],
title: json["title"],
systemfilename: json["systemfilename"],
transcriptionnotes: json["transcriptionnotes"],
mediatype: json["mediatype"],
originalfilename: json["originalfilename"],
locationname: json["locationname"],
);
Map<String, dynamic> toJson() => {
"locationid": locationid,
"mediaid": mediaid,
"title": title,
"systemfilename": systemfilename,
"transcriptionnotes": transcriptionnotes,
"mediatype": mediatype,
"originalfilename": originalfilename,
"locationname": locationname,
};
}
and then inside FutureBuilder use like this
FutureBuilder(
future: _locationData,
// Add <T> to the Asyncsnapshot class
builder: (BuildContext context, AsyncSnapshot<SomeClass> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.error != null) {
return const Center(child: Text('an error occured!'));
} else {
print(snapshot.data.data[0].longitude);
print(snapshot.data.media[0].title);
}
i am new to flutter, and i meet this API with complex "MAP" json. What i want is to display the list of countries with their details in flutter listview, How can i achieve that? Most of answers explain about "LIST" json.
{
"status": "Request is successful",
"message": null,
"data": {
"page": 1,
"last_page": 125,
"page_size": 2,
"countries": [
{
"id": "1",
"attributes": {
"name": "Grenada",
"code": "GD",
"subregion": "Caribbean",
"flag": "https://flagcdn.com/gd.svg",
"postalcode": "",
"latitude": "12.11666666",
"longitude": "-61.66666666",
"createdAt": "2023-01-11T22:15:40.000000Z",
"updatedAt": "2023-01-11T22:15:40.000000Z"
}
},
{
"id": "2",
"attributes": {
"name": "Malaysia",
"code": "MY",
"subregion": "South-Eastern Asia",
"flag": "https://flagcdn.com/my.svg",
"postalcode": "^(\\d{5})$",
"latitude": "2.5",
"longitude": "112.5",
"createdAt": "2023-01-11T22:15:40.000000Z",
"updatedAt": "2023-01-11T22:15:40.000000Z"
}
}
]
}
}
I found this GitHub project with these files json, modelClass Mainclass which relate with the concept but mine is has got one extra braces (map) so i do not know how to achieve the goal.
if there any suggestion or best way to code please help me.
this is how they created in model class but, but it does not work with me.
class Product {
final List<Result> results;
Product({this.results});
factory Product.fromJson(Map<String, dynamic> data) {
var list = data['data']['result'] as List;
List<Result> resultList = list.map((e) => Result.fromJson(e)).toList();
return Product(
results: resultList,
);
}
}
what i have done is
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
var data_from_link;
getData() async {
final String link = 'myurl';
data_from_link = await http.get(Uri.parse(link), headers: {"Accept": "application/json"});
final res = jsonDecode(data_from_link.body) as Map<String, dynamic>;
final List<Country> list= (res['data']['countries'] as List<dynamic>).map((e) => Country.fromJson(e))
.toList();
}
#override
void initState() {
super.initState();
getData();
}
#override
Widget build(BuildContext context) {
final res = jsonDecode(data_from_link.body) as Map<String, dynamic>;
final List<Country> list= (res['data']['countries'] as List<dynamic>).map((e) => Country.fromJson(e))
.toList();
return ListView.builder(
itemCount: list.length,
itemBuilder: (_, i) => ListTile(
title: Text(
list![i].attributes.name,
),
subtitle: Text(list![i].attributes.code),
)
);
}
}
You can create two classes for Country and Attribute
class Country {
const Country({required this.id, required this.attributes});
/// Creates a Country from Json map
factory Country.fromJson(Map<String, dynamic> json) => Country(
id: json['id'] as String,
attribute:
Attribute.fromJson(json['attributes'] as Map<String, dynamic>),
);
/// A description for id
final String id;
final Attribute attributes;
}
class Attribute {
const Attribute({
required this.name,
required this.code,
required this.createdAt,
required this.updatedAt,
});
/// Creates a Attribute from Json map
factory Attribute.fromJson(Map<String, dynamic> json) => Attribute(
name: json['name'] as String,
code: json['code'] as String,
createdAt: DateTime.parse(json['createdAt'] as String),
updatedAt: DateTime.parse(json['updatedAt'] as String),
);
final String name;
final String code;
final DateTime createdAt;
final DateTime updatedAt;
}
when decoding:
final res = jsonDecode(json) as Map<String, dynamic>;
final List<Country> list = (res['data']['countries'] as
List<dynamic>)
.map((e) => Country.fromJson(e))
.toList();
Thank you but how can i print or call data from country attribute
after decoding because when i try something like Print
(list.country.attribute.name) . I fail. My goal is to display on
Listview
You can use it like this:
ListView.builder(
itemCount: list.length,
itemBuilder: (_, i) => ListTile(
title: Text(
list[i].attributes.name,
),
subtitle: Text(list[i].attributes.code),
)),
UPDATE
import 'package:flutter/material.dart';
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
late Future<List<Country>> futureList;
Future<List<Country>?> getData() async {
final String link = 'yoururl';
final res = await http
.get(Uri.parse(link), headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
final List<Country> list = (res['data']['countries'] as List<dynamic>)
.map((e) => Country.fromJson(e))
.toList();
return list;
} else {
throw Exception('Failed to fetch data');
}
}
#override
void initState() {
super.initState();
futureList = getData();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: futureList,
builder: (context, snapshot) {
if (snapshot.hasData) {
final list = snapshot.data;
return ListView.builder(
itemCount: list!.length,
itemBuilder: (_, i) => ListTile(
title: Text(
list![i].attributes.name,
),
subtitle: Text(list![i].attributes.code),
),
);
} else if (snapshot.hasError) {
return const Text('error fetching data');
}
return const CircularProgressIndicator();
},
);
}
}
I want to deserialize some JSON data that contains a list of article information
{
"data": [
{
"id": 1,
"title": "First article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
},
{
"id": 2,
"title": "Second article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
}
]
}
No matter what the request is, the top level will have a key called data
So, I created a generic class called Entry
import 'package:json_annotation/json_annotation.dart';
part 'Entry.g.dart';
#JsonSerializable(genericArgumentFactories: true)
class Entry<TData> {
Entry(this.data);
TData data;
factory Entry.fromJson(Map<String, dynamic> json,TData Function(dynamic json) fromJsonTData) => _$EntryFromJson(json,fromJsonTData);
Map<String, dynamic> toJson(Object? Function(TData value) toJsonTData) => _$EntryToJson(this,toJsonTData);
}
And for an article, I created a class call NovelData
import 'dart:convert';
import 'dart:core';
import 'package:json_annotation/json_annotation.dart';
import 'Entry.dart';
part 'NovelData.g.dart';
#JsonSerializable(genericArgumentFactories: true)
class NovelData {
NovelData(this.id, this.title, this.createdDate, this.content, this.author, this.category);
int id;
String title;
String createdDate;
String content;
int author;
int category;
factory NovelData.fromJson(Map<String, dynamic> json) =>
_$NovelDataFromJson(json);
Map<String, dynamic> toJson() => _$NovelDataToJson(this);
}
Now, if I want to use the type like Entry<List<Novel>>> to deserialize the above JSON data, what should I do?
You can access them through the full path to the data.
Full path to your data: Map => key data => Array => Array index => Map
{}.data.[].0.{}
It only takes one class.
import 'package:fast_json/fast_json_selector.dart' as parser;
void main() async {
final path = '{}.data.[].0.{}';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final item = Novel.fromJson(event.lastValue as Map);
items.add(item);
event.lastValue = null;
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
final _json = '''
{
"data": [
{
"id": 1,
"title": "First article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
},
{
"id": 2,
"title": "Second article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
}
]
}''';
class Novel {
final int id;
final String title;
Novel({required this.id, required this.title});
#override
String toString() {
return title;
}
static Novel fromJson(Map json) {
return Novel(
id: json['id'] as int,
title: json['title'] as String,
);
}
}
Output:
First article
Second article
You can get the data before adding it to the list. The result is no different. Just a different path to the data.
void main() async {
final path = '{}.data.[].0';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final item = Novel.fromJson(event.lastValue as Map);
items.add(item);
event.lastValue = null;
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
This event follows the object creation event (at a lower event level):
JsonHandlerEvent.endObject => JsonHandlerEvent.element
You can get the data after adding it to the list. But it won't be as efficient.
void main() async {
final path = '{}.data.[]';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final list = event.lastValue as List;
items.addAll(list.map((e) => Novel.fromJson(e as Map)));
list.clear();
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
JsonHandlerEvent.endObject => JsonHandlerEvent.element => JsonHandlerEvent.endArray
Or even from property data. Very inefficient because all data is stored in memory.
void main() async {
final path = '{}.data';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final list = event.lastValue as List;
items.addAll(list.map((e) => Novel.fromJson(e as Map)));
event.lastValue = null;
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
JsonHandlerEvent.endObject => JsonHandlerEvent.element => JsonHandlerEvent.endArray => JsonHandlerEvent.endKey
I won't even write about the last level. There is no point in such an inefficient way. However, and in the previous one, too.
JsonHandlerEvent.endObject => JsonHandlerEvent.element => JsonHandlerEvent.endArray => JsonHandlerEvent.endKey => JsonHandlerEvent.endObject
There is a website which automatically generates all needed code from json. Here is example:
// To parse this JSON data, do
//
// final entry = entryFromJson(jsonString);
import 'dart:convert';
Entry entryFromJson(String str) => Entry.fromJson(json.decode(str));
String entryToJson(Entry data) => json.encode(data.toJson());
class Entry {
Entry({
this.data,
});
List<Datum> data;
factory Entry.fromJson(Map<String, dynamic> json) => Entry(
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
Datum({
this.id,
this.title,
this.createdDate,
this.content,
this.author,
this.category,
});
int id;
String title;
DateTime createdDate;
String content;
int author;
int category;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
id: json["id"],
title: json["title"],
createdDate: DateTime.parse(json["createdDate"]),
content: json["content"],
author: json["author"],
category: json["category"],
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"createdDate": createdDate.toIso8601String(),
"content": content,
"author": author,
"category": category,
};
}
you can try my jsonize package, it will handle any of your TData classes wherever they are in your Entry data list
import 'package:jsonize/jsonize.dart';
abstract class TData {}
class Entry implements Jsonizable {
Entry({
required this.data,
});
factory Entry.empty() => Entry(data: []);
List<TData> data;
#override
String get jsonClassCode => "Entry";
#override
Entry fromJson(json) => Entry(data: List<TData>.from(json["data"]));
#override
Map<String, dynamic> toJson() => {"data": data};
}
class NovelData extends TData implements Jsonizable {
NovelData({
required this.id,
required this.title,
required this.createdDate,
required this.content,
required this.author,
required this.category,
});
factory NovelData.empty() => NovelData(
id: 0,
title: "",
createdDate: DateTime(0),
content: "",
author: 0,
category: 0);
int id;
String title;
DateTime createdDate;
String content;
int author;
int category;
#override
String get jsonClassCode => "NovelData";
#override
NovelData fromJson(json) => NovelData(
id: json["id"],
title: json["title"],
createdDate: json["createdDate"],
content: json["content"],
author: json["author"],
category: json["category"],
);
#override
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"createdDate": createdDate,
"content": content,
"author": author,
"category": category,
};
}
main() {
Jsonize.registerClass(Entry.empty());
Jsonize.registerClass(NovelData.empty());
NovelData novel1 = NovelData(
id: 1,
title: "First article",
createdDate: DateTime.now(),
content: "Markdown content",
author: 1,
category: 1);
NovelData novel2 = NovelData(
id: 2,
title: "Second article",
createdDate: DateTime.now(),
content: "Markdown content",
author: 1,
category: 1);
Entry myEntry = Entry(data: [novel1, novel2]);
String myEntryJson = Jsonize.toJson(myEntry);
print(myEntryJson);
Entry entryBackToLife = Jsonize.fromJson(myEntryJson);
print(entryBackToLife);
}
jsonize can do more like handling enums. In your case benefits are:
DateTime serialization is handled by jsonize, you don not need to transform it
You can derive new classes from TData and put them into your Entry, jsonize will handle them and automatically transform it back to the original class
i'm trying to get media fields from the instagram api and i'm getting this error
type 'String' is not a subtype of type 'int' of 'index'
here's Homepage.dart :
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'package:insta_details/models/data.dart';
import 'package:insta_details/utils/custom_dio_mixin.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
static String id = "HomePage";
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with CustomDioMixin {
bool loading = true;
bool error = false;
late Media media;
#override
void initState() {
super.initState();
getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: loading
? const Center(
child: CircularProgressIndicator(),
)
: error
? const Center(
child: Text('An error has occurred!'),
)
: ListView.builder(
itemBuilder: (context, index) => MediaWidget(
media: media,
),
),
),
);
}
Future<void> getData() async {
try {
final storage = GetStorage();
final token = storage.read("accessToken");
Media? media;
final response = await dio.get(
'https://graph.instagram.com/me/media?fields=id,caption,media_url,timestamp&access_token=$token',
);
print("get data response => ${response.statusCode} ${response.data}");
Media mediadata = Media.fromJson(response.data);
print(mediadata);
} catch (e) {
print("get data failed");
print(e);
setState(() {
error = true;
});
} finally {
setState(() {
loading = false;
});
}
}
}
class MediaWidget extends StatelessWidget {
final Media media;
const MediaWidget({Key? key, required this.media}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: 6,
itemBuilder: (context, index) {
return Image.network(media.mediaUrl);
},
);
}
}
and here data.dart :
class Media {
Media({
required this.id,
required this.caption,
required this.mediaUrl,
required this.timestamp,
});
String id;
String caption;
String mediaUrl;
String timestamp;
factory Media.fromJson(Map<String, dynamic> json) => Media(
id: json["data"]["id"] as String,
caption: json["data"]["caption"] as String,
mediaUrl: json["data"]["media_url"] as String,
timestamp: json["data"]["timestamp"] as String,
);
Map<String, dynamic> toJson() => {
"id": id,
"caption": caption,
"media_url": mediaUrl,
"timestamp": timestamp,
};
}
and the log :
I/flutter ( 5699): get data response => 200 {data: [{id: 18106429915287733, caption: cabin in the woods, media_url: https://scontent.cdninstagram.com/v/t51.29350-15/272751472_358111429123560_6575694365508668882_n.jpg?_nc_cat=100&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=0omRv4cUGtwAX8bbmC7&_nc_ht=scontent.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT_fqBkL5ykJXWRj7Rcy4nCnyuXEKh-8o0TX9FJkJ4dcfQ&oe=61FD881A, timestamp: 2022-01-27T11:15:07+0000}, {id: 17917394609104775, caption: Truck, media_url: https://scontent.cdninstagram.com/v/t51.29350-15/272701475_1080001635904581_1705933746471766077_n.jpg?_nc_cat=107&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=ZfSpeg7rHn4AX89PW0c&_nc_ht=scontent.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT_Qbj7zOH-UEzplA9mdIrCHaeb9EBBuz1RjKJclN9Q2RA&oe=61FE9898, timestamp: 2022-01-27T11:14:26+0000}, {id: 17921627228176014, caption: Gaara, media_url: https://scontent.cdninstagram.com/v/t51.29350-15/272660463_892749041374464_5507853711157520506_n.jpg?_nc_cat=101&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=va5seINOs-4AX9vOy4L&_nc_ht=scontent.cdninst
I/flutter ( 5699): get data failed
I/flutter ( 5699): type 'String' is not a subtype of type 'int' of 'index'
my JSON response :
{
"data": [
{
"id": "18106429915287733",
"caption": "cabin in the woods",
"media_type": "IMAGE",
"media_url": "https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272751472_358111429123560_6575694365508668882_n.jpg?_nc_cat=100&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=xXGDvxMsycAAX_U_-55&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT-EuNnLxrBSNirBOl1prRXlHepdhQqUjYRBEv3Zh_Ld6Q&oe=61FD881A",
"username": "parekchampl",
"timestamp": "2022-01-27T11:15:07+0000"
},
{
"id": "17917394609104775",
"caption": "Truck",
"media_type": "IMAGE",
"media_url": "https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272701475_1080001635904581_1705933746471766077_n.jpg?_nc_cat=107&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=ZfSpeg7rHn4AX_J_eQs&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT92hbhb0XK56kxC-_e8kpM6QFLazDH0TDCfdIdEIpNinw&oe=61FC9E58",
"username": "parekchampl",
"timestamp": "2022-01-27T11:14:26+0000"
},
{
"id": "17921627228176014",
"caption": "Gaara",
"media_type": "IMAGE",
"media_url": "https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272660463_892749041374464_5507853711157520506_n.jpg?_nc_cat=101&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=va5seINOs-4AX_SB6jL&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT8rviJ6wbaT0yF1Hq2VprtnQ-W0rARS5oxIr52MIhC0Rw&oe=61FD720B",
"username": "parekchampl",
"timestamp": "2022-01-27T11:13:42+0000"
},
{
"id": "18024346318348836",
"caption": "Marceline",
"media_type": "IMAGE",
"media_url": "https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272807293_686124672409566_4991399943515126026_n.jpg?_nc_cat=106&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=JMfTMSD_1c8AX-m5WDx&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT_P38eDVtcqEYL053wGPkLjhHStLCh7_fgFnCg4LcH1yA&oe=61FD1F82",
"username": "parekchampl",
"timestamp": "2022-01-27T11:13:02+0000"
},
{
"id": "17859174368680579",
"caption": "uchiha shisui",
"media_type": "IMAGE",
"media_url": "https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272721151_749467822692662_5191995429373550055_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=01A68vtgY-kAX-ux6iB&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT9oWtK9VWV8j3c8Ij2YXctIpuh9sC-NJO1BLCwFObDDSA&oe=61FE0B03",
"username": "parekchampl",
"timestamp": "2022-01-27T11:12:35+0000"
},
{
"id": "17917757036265369",
"caption": "Son and Father",
"media_type": "IMAGE",
"media_url": "https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272660947_1107548556714461_1575953024252145708_n.jpg?_nc_cat=100&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=Mzj5Wp9sv_oAX_2Z4Nv&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT8Ywp3DUIemrIoCPajFvivfTG_-AWvEs2fpkngYXUN6Lg&oe=61FE17A1",
"username": "parekchampl",
"timestamp": "2022-01-27T11:11:47+0000"
}
],
"paging": {
"cursors": {
"before": "QVFIUnpEcERJTXdYRjd3SVp3MUo2U25UeWhhdlgxQ2xMY0diR2pYVFhCVl9TUUhlM1hqYllKWUpEWXJtYW5RWW41am1Lc3B4U281TU14ZAFoxSVBkMVRsZAkZAB",
"after": "QVFIUkgtUzExdDNsYzgwUFhGdnRXQlB6N0JkZATVFeU1DVkhzXzduLTF1RklpR1A5MDNMeWVEemtzdE15OVBlYmpYb29mQlVtdDJsX1N2SUcwa2ZAkc21jblZAn"
}
}
}
According to the documentation : https://developers.facebook.com/docs/instagram-basic-display-api/reference/media ,every field is a String
factory Media.fromJson(Map<String, dynamic> json) => Media(
id: json["data"]["id"] as String,
caption: json["data"]["caption"] as String,
mediaUrl: json["data"]["media_url"] as String,
timestamp: json["data"]["timestamp"] as String,
);
Or change to something like below:
factory Media.fromJson(Map<String, dynamic> json) => Media(
id: int.parse(json["data"]["id"] as String),
caption: json["data"]["caption"] as String,
mediaUrl: json["data"]["media_url"] as String,
timestamp: DateTime.parse(json["data"]["timestamp"] as String),
);
The value of your data key is a List not a Map. Here's an example:
const responseData =
{
"data":[
{
"id":"18106429915287733",
"caption":"cabin in the woods",
"media_type":"IMAGE",
"media_url":"https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272751472_358111429123560_6575694365508668882_n.jpg?_nc_cat=100&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=xXGDvxMsycAAX_U_-55&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT-EuNnLxrBSNirBOl1prRXlHepdhQqUjYRBEv3Zh_Ld6Q&oe=61FD881A",
"username":"parekchampl",
"timestamp":"2022-01-27T11:15:07+0000"
},
{
"id":"17917394609104775",
"caption":"Truck",
"media_type":"IMAGE",
"media_url":"https://scontent-iad3-1.cdninstagram.com/v/t51.29350-15/272701475_1080001635904581_1705933746471766077_n.jpg?_nc_cat=107&ccb=1-5&_nc_sid=8ae9d6&_nc_ohc=ZfSpeg7rHn4AX_J_eQs&_nc_ht=scontent-iad3-1.cdninstagram.com&edm=ANo9K5cEAAAA&oh=00_AT92hbhb0XK56kxC-_e8kpM6QFLazDH0TDCfdIdEIpNinw&oe=61FC9E58",
"username":"parekchampl",
"timestamp":"2022-01-27T11:14:26+0000"
}
]
};
void main() {
final mediaList = responseData["data"]!.map((entry) => Media.fromJson(entry))
.toList();
for (var media in mediaList) {
print(media.id);
}
}
class Media {
Media({
required this.id,
required this.caption,
required this.mediaUrl,
required this.timestamp,
});
int? id;
String caption;
String mediaUrl;
String timestamp;
factory Media.fromJson(Map<String, dynamic> json) {
return Media(
id: int.tryParse(json["id"]),
caption: json["caption"],
mediaUrl: json["media_url"],
timestamp: json["timestamp"],
);
}
Map<String, dynamic> toJson() => {
"id": id.toString(),
"caption": caption,
"media_url": mediaUrl,
"timestamp": timestamp,
};
}
On your model class id is nullable int, but you are receiving id as String.
You can typecast string to int On your Media.fromJson, while the id accept null value, if something wrong happen id will get null value.
factory Media.fromJson(Map<String, dynamic> json) => Media(
id: int.tryParse(json["data"]["id"]),
And for toJson pass id as String
Map<String, dynamic> toJson() => {
"id": id.toString(),
More about int.tryParse.
HERE IS THE JSON OF RESPONSE BODY:
{
"status": "success",
"contents": [{
"id": "15",
"cname": "DOGS",
"dogs_image": "1638695967-rtyyyt.jpg",
"cat_image": "1638695967-jhjjj.jpg",
"sub_category": [{
"subcatid": "36",
"cat_id": "15",
"sub_category_name": "Accessories",
"banner": null,
"image": "1638695982-ACCESORIE.jpg"
}, {
"subcatid": "39",
"cat_id": "15",
"sub_category_name": "Beds",
"banner": null,
"image": "1638695982-ACCESORIE.jpg"
}]
}, {
"id": "14",
"cname": "CATS",
"dogs_image": "1638695967-rtyyyt.jpg",
"cat_image": "1638695967-jhjjj.jpg",
"sub_category": [{
"subcatid": "47",
"cat_id": "14",
"sub_category_name": "Accessories",
"banner": null,
"image": "1638695982-ACCESORIE.jpg"
}]
}]
}
// API call to get the above json data:
Future<List<CatListData>> dashboardDataAPI(http.Client client) async {
final response = await client.get(Uri.parse(Utilities.BASE_URL));
List list = json.decode(response.body)['contents'];
return parsePhotos(list.toString());
}
// A function that converts a response body into a List
List<CatListData> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<CatListData>((json) => CatListData.fromJson(json)).toList();
}
// Cat List Class
class CatListData{
final String id;
final String cName;
final String dogImage;
final String catImage;
final List<SubCatListData> subCatListDataList;
CatListData({required this.id, required this.cName, required this.dogImage, required this.catImage, required this.subCatListDataList});
factory CatListData.fromJson(Map<String, dynamic> json) {
return CatListData(
id: json['id'] as String,
cName: json['cname'] as String,
dogImage: json['dogs_image'] as String,
catImage: json['cat_image'] as String,
subCatListDataList: List<SubCatListData>.from(json['sub_category'] as Iterable),
);
}
}
// Sub Cat Class
class SubCatListData{
final String subCatId;
final String catId;
final String subCategoryName;
final String banner;
final String image;
SubCatListData({required this.subCatId, required this.catId, required this.subCategoryName, required this.banner, required this.image});
factory SubCatListData.fromJson(Map<String, dynamic> json) {
return SubCatListData(
subCatId: json['subcatid'] as String,
catId: json['cat_id'] as String,
subCategoryName: json['sub_category_name'] as String,
banner: json['banner'] as String,
image: json['image'] as String,
);
}
}
Here showing null when I print snapshot
Container(
child: FutureBuilder<List<CatListData>>(
future: dashboardDataAPI(http.Client()),
builder: (context, snapshot) {
print("Response:: "+snapshot.data.toString());
if (snapshot.hasData) {
return PhotosList(photos: snapshot.data!);
}else if(snapshot.hasError){
return const Center(
child: Text('An error has occurred!'),);
}else{
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
)
Please let me know how I can fix this issue and parse the multiple json array data into the list.
Thanks
I believe the problem happens on this line on the CatListData.fromJson constructor:
subCatListDataList: List<SubCatListData>.from(json['sub_category'] as Iterable),
you never call SubCatListData.fromJson, I believe this will work better for your assignment:
subCatListDataList: (json['sub_category'] as Iterable).map<SubCatListData>(
(value) => SubCatListData.fromJson(value as Map<String, dynamic>),
),