flutter nested json array of object parsing - json

Hi i am building an app and trying to parse nested array of object from an api call back and getting this error
Type String is not the sub Type of Map<String, dynamic>
Here is the model class
class Tournament {
String id;
String title;
String roomID;
String roomPass;
String map;
String type;
String date;
String time;
int joined;
String createdBy;
List<UserIds> joinedUsers;
Tournament(
{this.createdBy,
this.joinedUsers,
this.id,
this.date,
this.map,
this.roomID,
this.roomPass,
this.time,
this.title,
this.type,
this.joined});
factory Tournament.fromJson(Map<String, dynamic> json) {
var list = json['joinedUsers'] as List;
List<UserIds> userList =
list.map((data) => UserIds.fromJson(data)).toList();
return Tournament(
id: json['_id'],
title: json['title'],
roomID: json['roomId'],
roomPass: json['roomPass'],
map: json['mapType'],
type: json['type'],
date: json['date'],
time: json['time'],
joined: json['joined'],
createdBy: json['createdBy'],
joinedUsers: userList);
}
}
class UserIds {
String userId;
UserIds({this.userId});
factory UserIds.fromJson(Map<String, dynamic> parsedJson) {
return UserIds(userId: parsedJson['\$oid']);
}
}
this is the json call back i got
{
"_id": {
"$oid": "5f1c47f2c3c051d9828b1697"
},
"joinedUsers": [{
"$oid": "5f18621d6fca9d3e70a9fabe"
}, {
"$oid": "5f1a7609f7f69d2a1064e5ec"
}],
"title": "HomeComing",
"date": "20-02-2020",
"time": "8:22 Am",
"roomId": "12345",
"roomPass": "12223",
"joined": {
"$numberInt": "9"
},
"mapType": "Erangle",
"type": "Dual",
"createdBy": {
"$oid": "5f16d9bde0fd621dec10e1c5"
},
"__v": {
"$numberInt": "0"
}
}
now when i run this it gave me an error that type String is not the sub type of Map<String, dynamic>
i dont know what wrong i am doing right now

Could you try:
list.map((data) => UserIds.fromJson(JSON.parse(data)).toList();
Could be parse problems, you could log first your elements in your lambda by this to verify what it gives:
list.map((element) =>
{
console.log(data);
//UserIds.fromJson(JSON.parse(data)).toList();
});

Just check out this example that I have made for userIds:
I have taken your json locally.
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class UserIds {
String userId;
UserIds({this.userId});
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _isLoading = false;
List<UserIds> userIdsList = List();
#override
void initState() {
super.initState();
getData();
}
getData() async {
String data =
await DefaultAssetBundle.of(context).loadString("json/parse.json");
Map jsonData = json.decode(data);
jsonData['joinedUsers'].forEach((item) {
item.forEach((key, value) {
print('This is the Key $key value $value');
userIdsList.add(UserIds(userId: value));
});
});
print('This is the list ${userIdsList.length}');
}
#override
Widget build(BuildContext context) {
return Scaffold(body: Text(''));
}
}

Related

How can i pass this complex Json MAP Data into flutter Listview

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

How to deserialize generic JSON with List type in Dart

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

Stuck with json in flutter

I dont know the problem is because when debuging there isn't error info but the value of json is empty when I print info.lenght
class _HomePageState extends State<HomePage> {
List info = [];
_initData(){
DefaultAssetBundle.of(context).loadString("json/info.json").then((value){
info = jsonDecode(value);
});
}
#override
void iniState(){
super.initState();
_initData();
}
and below is my json code in json/info.json
[
{
"title": "Glue",
"img": "assets/ex1.png"
},
{
"title": "Abs",
"img": "assets/ex2.png"
},
{
"title": "Legs",
"img": "assets/ex3.png"
},
{
"title": "Arms",
"img": "assets/ex4.png"
}
]
and how to we print the img and title value of json in dart?
You have to first create a model class for your json response.
// To parse this JSON data, do
//
// final jsonModel = jsonModelFromJson(jsonString);
import 'dart:convert';
List<JsonModel> jsonModelFromJson(String str) => List<JsonModel>.from(json.decode(str).map((x) => JsonModel.fromJson(x)));
String jsonModelToJson(List<JsonModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class JsonModel {
JsonModel({
this.title,
this.img,
});
String title;
String img;
factory JsonModel.fromJson(Map<String, dynamic> json) => JsonModel(
title: json["title"] == null ? null : json["title"],
img: json["img"] == null ? null : json["img"],
);
Map<String, dynamic> toJson() => {
"title": title == null ? null : title,
"img": img == null ? null : img,
};
}
then just use this model class to decode your json file.
var result = JsonModel.fromJson(jsonResponse);
Later, you can just use
result.title or result.img to get the data
You have to add your json file into pubspec.yaml file.
flutter:
assets:
- json/info.json
Before loading page view, you should load values from your json file and assign a list in initState. After that, you can use list as map by [index][header].
class JsonResult extends StatefulWidget {
const JsonResult({Key? key}) : super(key: key);
#override
_JsonResultState createState() => _JsonResultState();
}
class _JsonResultState extends State<JsonResult> {
var jsonList = [];
#override
void initState() {
rootBundle.loadString("json/info.json").then((value) {
jsonList = json.decode(value);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView.builder(
itemBuilder: (context, index) {
return ListTile(
leading: Image.asset(jsonList[index]["img"]),
title: Text(jsonList[index]["title"]),
);
},
itemCount: jsonList.length,
),
),
);
}
}

Store Deserialized json in a list of objects Flutter

I have successfully deserialized my json file. I have stored one element of the json in one object successfully , but i am getting a problem storing the objects in a list.
I tried every possible solution from the internet below you will see the trials i have made.
This is my code
class _MyHomePageState extends State<MyHomePage> {
String? _chosenSubCounty;
List<County> counties = [];
Future<String> getJson() async {
final jsonResult = await rootBundle.loadString('assets/json_files/counties.json');
List<dynamic> parsedListJson = jsonDecode(jsonResult);
print(parsedListJson[0]);//prints {name: Baringo, capital: Kabarnet, code: 30, sub_counties: [Baringo central, Baringo north, Baringo south, Eldama ravine, Mogotio, Tiaty]}
final county = County.fromJson(parsedListJson[0]);
print(county.name.toString());//prints Baringo
//trial no 1 failed
counties = parsedListJson.map((i)=>County.fromJson(i)).toList();
//trial no 2 also failed
counties = List<County>.from(parsedListJson.map((i) => County.fromJson(i)));
//trial no 3 also failed
for(int i = 0; i < parsedListJson.length; i++){
counties.add(County.fromJson(parsedListJson[i]));
}
print(counties);//prints Error: Expected a value of type 'String', but got one of type 'Null'
return jsonResult;
}
#override
void initState() {
getJson();
}
#override
Widget build(BuildContext context) {..........}
}
This is Model Class
import 'dart:convert';
List<County> countyFromJson(String str) => List<County>.from(json.decode(str).map((x) => County.fromJson(x)));
String countyToJson(List<County> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class County {
String name;
String capital;
int code;
List subCounties;
County({
required this.name,
required this.capital,
required this.code,
required this.subCounties,
});
factory County.fromJson(Map<String, dynamic> json) {
return County(
name: json["name"],
capital: json["capital"],
code: json["code"],
subCounties: List<String>.from(json["sub_counties"])
);
}
Map<String, dynamic> toJson() => {
"name": name,
"capital": capital == null ? null : capital,
"code": code,
"sub_counties": List<dynamic>.from(subCounties.map((x) => x)),
};
}
This is the json file
[
{
"name": "Baringo",
"capital": "Kabarnet",
"code": 30,
"sub_counties": [
"Baringo central",
"Baringo north",
"Baringo south",
"Eldama ravine",
"Mogotio",
"Tiaty"
]
},
{
"name": "Bomet",
"capital": "Bomet",
"code": 36,
"sub_counties": [
"Bomet central",
"Bomet east",
"Chepalungu",
"Konoin",
"Sotik"
]
},
]
First remove comma after your 2nd json Object from your json file. Then use this code to parse your json. I've run the project and it works totally fine.
import 'package:flutter/material.dart';
import 'package:stacksolution/county_model.dart';
class FetchData extends StatefulWidget {
const FetchData({Key? key}) : super(key: key);
#override
_FetchDataState createState() => _FetchDataState();
}
class _FetchDataState extends State<FetchData> {
#override
void initState() {
// TODO: implement initState
callJson();
super.initState();
}
callJson() async {
String jsonString =
await DefaultAssetBundle.of(context).loadString('assets/county.json');
List<CountyModel> list = countyFromJson(jsonString);
print("$list");
}
#override
Widget build(BuildContext context) {
return Container();
}
}
The Model class:
import 'dart:convert';
List<CountyModel> countyFromJson(String str) => List<CountyModel>.from(
json.decode(str).map((x) => CountyModel.fromJson(x)));
String countyToJson(List<CountyModel> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class CountyModel {
CountyModel({
this.name,
this.capital,
this.code,
this.subCounties,
});
String? name;
String? capital;
int? code;
List<String>? subCounties;
factory CountyModel.fromJson(Map<String, dynamic> json) => CountyModel(
name: json["name"],
capital: json["capital"],
code: json["code"],
subCounties: List<String>.from(json["sub_counties"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"name": name,
"capital": capital,
"code": code,
"sub_counties": List<dynamic>.from(subCounties!.map((x) => x)),
};
}
The Json File:
[
{
"name": "Baringo",
"capital": "Kabarnet",
"code": 30,
"sub_counties": [
"Baringo central",
"Baringo north",
"Baringo south",
"Eldama ravine",
"Mogotio",
"Tiaty"
]
},
{
"name": "Bomet",
"capital": "Bomet",
"code": 36,
"sub_counties": [
"Bomet central",
"Bomet east",
"Chepalungu",
"Konoin",
"Sotik"
]
}
]

Mapping response.body to model class

I'm trying to map response to model class and then show all elements one by one, so that user can scroll down. Response goes like that:
{"items":[{"id":"bd9acd37-5c91-47cb-a71e-0e43fb2f7882","imageUrl":"","title":"fak hez as","city":"Gdansk"},{"id":"210ad8c8-d279-4c38-845c-34c772cfec21","imageUrl":"","title":"Some random title :)","city":"Warsaw"}],"total":2,"pageNumber":1}
And my model which I have generated with https://plugins.jetbrains.com/plugin/12562-jsontodart-json-to-dart-
class Advertisement {
List<Items>? _items;
int? _total;
int? _pageNumber;
List<Items>? get items => _items;
int? get total => _total;
int? get pageNumber => _pageNumber;
Advertisement({List<Items>? items, int? total, int? pageNumber}) {
_items = items;
_total = total;
_pageNumber = pageNumber;
}
Advertisement.fromJson(dynamic json) {
if (json["items"] != null) {
_items = [];
json["items"].forEach((v) {
_items?.add(Items.fromJson(v));
});
}
_total = json["total"];
_pageNumber = json["pageNumber"];
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
if (_items != null) {
map["items"] = _items?.map((v) => v.toJson()).toList();
}
map["total"] = _total;
map["pageNumber"] = _pageNumber;
return map;
}
}
/// id : "bd9acd37-5c91-47cb-a71e-0e43fb2f7882"
/// imageUrl : ""
/// title : "fak hez as"
/// city : "Gdansk"
class Items {
String? _id;
String? _imageUrl;
String? _title;
String? _city;
String? get id => _id;
String? get imageUrl => _imageUrl;
String? get title => _title;
String? get city => _city;
Items({String? id, String? imageUrl, String? title, String? city}) {
_id = id;
_imageUrl = imageUrl;
_title = title;
_city = city;
}
Items.fromJson(dynamic json) {
_id = json["id"];
_imageUrl = json["imageUrl"];
_title = json["title"];
_city = json["city"];
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
map["id"] = _id;
map["imageUrl"] = _imageUrl;
map["title"] = _title;
map["city"] = _city;
return map;
}
}
My screen:
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:.../constants/AppConstants.dart';
import 'package:.../ui/pages/home/page/Advertisement.dart';
import 'package:.../util/HttpActions.dart';
import 'BottomAppBar.dart';
import 'FAB.dart';
import 'Advertisement.dart';
class HomePage extends StatefulWidget {
final String jwt;
const HomePage(this.jwt);
#override
_HomePage createState() => _HomePage();
factory HomePage.fromBase64(String jwt) => HomePage(jwt);
}
class _HomePage extends State<HomePage> {
late final String jwt;
#override
void initState() {
super.initState();
jwt = widget.jwt;
}
#override
Widget build(BuildContext context) => Scaffold(
body: Scaffold(
backgroundColor: const Color(0xFEF9F9FC),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: buildFAB(),
bottomNavigationBar: BuildBottomAppBar(),
body: Container(
padding: EdgeInsets.all(35.0),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: [
FutureBuilder(
future: fetchAdvertisements(),
builder: (context, snapshot) => snapshot.hasData
? Text("")
: snapshot.hasError
? Text("An error occurred")
: CircularProgressIndicator(),
),
],
),
),
),
);
Future<List<Advertisement>?> fetchAdvertisements() async {
var response = await HttpActions.makeHttpGet(
{},
AppConstants.ADVERTISEMENTS_ENDPOINT,
{HttpHeaders.contentTypeHeader: HttpActions.APPLICATION_JSON_HEADER});
print(response.statusCode);
if (response.statusCode == 200) {
print(response.body);
final responseList = json.decode(response.body);
print(responseList);
List<Advertisement> z = (json.decode(response.body) as List)
.map((i) => Advertisement.fromJson(i))
.toList();
print(z);
return null;
} else {
throw Exception('Failed to load post');
}
}
}
Error occurs while executing this line:
List<Advertisement> z = (json.decode(response.body) as List)
.map((i) => Advertisement.fromJson(i))
.toList();
it does not map to this List
While debugging I get:
What I'm making wrong? Is there something wrong with model class?
One more mistake that you are making with your code, You are calling fetchAdvertisement() method inside a build(BuildContext context). This means everytime build() method gets called. It will call fetchAdvertisement() this as well.
Column(
children: [
FutureBuilder(
future: fetchAdvertisements(),
builder: (context, snapshot) => snapshot.hasData
? Text("")
: snapshot.hasError
? Text("An error occurred")
: CircularProgressIndicator(),
),
],
),
To avoid such an issue.
late Function _fetchAdvertisements;
void initState() {
super.initState();
_fetchAdvertisements = fetchAdvertisements(); }
Column(
children: [
FutureBuilder(
future: _fetchAdvertisements,
builder: (context, snapshot) => snapshot.hasData
? Text("")
: snapshot.hasError
? Text("An error occurred")
: CircularProgressIndicator(),
),
],
),
You cannot simply parse this with cast as List. Because your response isn't even a List.
{
"items": [
{
"id": "bd9acd37-5c91-47cb-a71e-0e43fb2f7882",
"imageUrl": "",
"title": "fak hez as",
"city": "Gdansk"
},
{
"id": "210ad8c8-d279-4c38-845c-34c772cfec21",
"imageUrl": "",
"title": "Pozdrawiam z Łodzi :)",
"city": "Warsaw"
}
],
"total": 2,
"pageNumber": 1
}
For this response, you can parse this like this:
Advertisement.fromJson(json.decode(response.body))
But now you have only one item of Advertisement.
If your response were like this, with multiple advertisements:
{
"advertisements": [
{
"items": [
{
"id": "bd9acd37-5c91-47cb-a71e-0e43fb2f7882",
"imageUrl": "",
"title": "fak hez as",
"city": "Gdansk"
},
{
"id": "212ad8c8-d279-4c38-845c-34c772cfec21",
"imageUrl": "",
"title": "Great city!",
"city": "Łódź"
}
],
"total": 2,
"pageNumber": 1
},
{
"items": [
{
"id": "bd9acd37-5c91-47cb-a71e-0e43fb2f7882",
"imageUrl": "",
"title": "fak hez as",
"city": "Gdansk"
},
{
"id": "210ad8c8-d279-4c38-845c-34c772cfec21",
"imageUrl": "",
"title": "Some random title :)",
"city": "Warsaw"
}
],
"total": 2,
"pageNumber": 1
}
]
}
You can parse this simply with this code:
List<Advertisement> advertisements = [
for (final advertisement in json.decode(response2)['advertisements'])
Advertisement.fromJson(advertisement)
];
You can learn how to do JSON serialization properly with JsonSerializable I recommend you to read this here.
I just copied you responded.body text and showed you a dummy example. Everything will be done in the same way, weather you use an HTTP calls. Always make sure that you know what response is coming.
class __AppState extends State<_App> {
#override
void initState() {
// TODO: implement initState
super.initState();
convertApiResponse();
}
Future<String> getApiJson() async {
return '{"items":[{"id":"bd9acd37-5c91-47cb-a71e-0e43fb2f7882","imageUrl":"","title":"fak hez as","city":"Gdansk"},{"id":"210ad8c8-d279-4c38-845c-34c772cfec21","imageUrl":"","title":"Some random title :)","city":"Warsaw"}],"total":2,"pageNumber":1} ';
}
Future<void> convertApiResponse() async {
final data = await getApiJson();
final decodedData = json.decode(data) as Map;
Advertisement x = Advertisement.fromJson(decodedData);
x._items!.forEach((element) {
log("Item-> City: ${element.city}");
log("Item->ID: ${element.id}");
log("Item->ImageURL: ${element.imageUrl}");
log("Item->Title: ${element.title}");
});
log("Page Number: ${x.pageNumber}");
log("Total: ${x.total}");
}
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold());
}
}
Untouched Items.dart
class Items {
String? _id;
String? _imageUrl;
String? _title;
String? _city;
String? get id => _id;
String? get imageUrl => _imageUrl;
String? get title => _title;
String? get city => _city;
Items({String? id, String? imageUrl, String? title, String? city}) {
_id = id;
_imageUrl = imageUrl;
_title = title;
_city = city;
}
Items.fromJson(dynamic json) {
_id = json["id"];
_imageUrl = json["imageUrl"];
_title = json["title"];
_city = json["city"];
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
map["id"] = _id;
map["imageUrl"] = _imageUrl;
map["title"] = _title;
map["city"] = _city;
return map;
}
}
Untouched Advertisement.dart
class Advertisement {
List<Items>? _items;
int? _total;
int? _pageNumber;
List<Items>? get items => _items;
int? get total => _total;
int? get pageNumber => _pageNumber;
Advertisement({List<Items>? items, int? total, int? pageNumber}) {
_items = items;
_total = total;
_pageNumber = pageNumber;
}
Advertisement.fromJson(dynamic json) {
if (json["items"] != null) {
_items = [];
json["items"].forEach((v) {
_items?.add(Items.fromJson(v));
});
}
_total = json["total"];
_pageNumber = json["pageNumber"];
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
if (_items != null) {
map["items"] = _items?.map((v) => v.toJson()).toList();
}
map["total"] = _total;
map["pageNumber"] = _pageNumber;
return map;
}
}
Here is a picture that resolves your fetchAdvertisements() call on every build