How to read List<Model class> in Nested structures with Lists? - dart

How to get one by one values in Nested structures with Lists json structure.
controller.dart
final jsonResponse = json.decode(response.body);
AssetRegister model = AssetRegister.fromJson(jsonResponse);
print(model.data);
how to print like this print(model.dart.i);//error
model.dart
class AssetRegister {
final List<Data> data;
AssetRegister({this.data});
factory AssetRegister.fromJson(Map<String, dynamic> json) {
var list = json['data'] as List;
print(list.runtimeType);
List<Data> assetList = list.map((i) => Data.fromJson(i)).toList();
return AssetRegister(data: assetList);
}
}
class Data {
final int i;
final String d;
Data({this.i, this.d});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
i: json['i'],
d: json['d'],
);
}
}

You should provide toString method implementation for Data class like this:
class Data {
final int i;
final String d;
Data({this.i, this.d});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
i: json['i'],
d: json['d'],
);
}
#override
String toString() => return 'i = $i, $d';
}

Related

Why it returns Instance of instead of the value?

Why the getCurrencyFromAPI function returns Intance of currency instead of the value itself. Is there some thing wrong with my model class?
This is the function
import 'dart:convert';
import 'package:app_bloc/data/models/currency.dart';
import 'package:http/http.dart' as http;
import 'package:app_bloc/constants/api_urls.dart';
class Repository {
Future<dynamic> getCurrencyFromAPI() async {
final res = await http.get(Uri.parse(coinbaseURL));
if (res.statusCode == 200) {
final resData = jsonDecode(res.body);
final data = resData['data'] as List;
List<Currency> list = [];
for (var e in data) {
final a = Currency.fromJson(e);
list.add(a);
}
print(list);
} else {
throw Exception('Error fetching data from API');
}
}
}
void main(List<String> args) {
Repository repo = Repository();
repo.getCurrencyFromAPI();
}
this is the model class
class Currency {
String id;
String name;
String minSize;
Currency({required this.id, required this.name, required this.minSize});
factory Currency.fromJson(Map<String, dynamic> data) {
final id = data['id'] as String;
final name = data['name'] as String;
final minSize = data['min_size'] as String;
return Currency(id: id, name: name, minSize: minSize);
}
}
Your Currency class does not have a toString method. That means it inherits the default from Object which returns Instance of 'Currency'.
When you print the List<Currency> it calls toString on every element to get a string representation. So, that's what you see. It is a Currency object.
Try adding:
String toString() => "Currency(id: $id, name: $name, minSize: $minSize)";
to you Currency class and see if it makes a difference.
Currency currencyModelFromJson(String str) => Currency.fromJson(json.decode(str));
class Currency {
String id;
String name;
String minSize;
Currency({required this.id, required this.name, required this.minSize});
factory Currency.fromJson(Map<String, dynamic> data) {
final id = data['id'] as String;
final name = data['name'] as String;
final minSize = data['min_size'] as String;
return Currency(id: id, name: name, minSize: minSize);
}
}
Then do this :
class Repository {
Future<dynamic> getCurrencyFromAPI() async {
final res = await http.get(Uri.parse(coinbaseURL));
if (res.statusCode == 200) {
final resData = jsonDecode(res.body);
final data = resData['data'] as List;
List<Currency> list = [];
for (var e in data) {
final a = currencyModelFromJson(e); // change here
list.add(a);
}
print(list);
} else {
throw Exception('Error fetching data from API');
}
}
}

How to define a factory method in dart abstracts

I want to extend or implement some BaseModel so that I can summarize some hard coded methods exist in a lot of my classes.
So I want to declare some methods and factory methods as static and required methods in all the inherited classes.
The following is a draft of what I tried.
How can I do that properly?
#immutable
abstract class BaseDataModel {
// BaseDataModel();
}
abstract class DataModel{
DataModel();
factory DataModel.fromJson(Map json) => ConcreteDataModel();
Map<String, dynamic> toJson() => {};
}
class ConcreteDataModel extends DataModel{
}
// extension DataModelJsonHandler on List<DataModel> {
// List<DataModel> itemsFromJson(String json) {
// List<DataModel> modelItems = [];
// List jsonlist = jsonDecodeSafeToList(json);
// jsonlist.forEach((item) {
// modelItems.add(DataModel.fromJson(item));
// });
// return modelItems;
// }
// String itemsToJson() {
// final List jsonlist = [];
// forEach((item) {
// jsonlist.add(item.toJson());
// });
// return jsonEncodeSafe(jsonlist);
// }
// }
class DataModelJsonHandler {
static List<DataModel> itemsFromJson(String json) {
List<DataModel> items = [];
List jsonlist = jsonDecodeSafeToList(json);
jsonlist.forEach((item) {
items.add(DataModel.fromJson(item));
});
return items;
}
static String itemsToJson(List<DataModel> items) {
final List jsonlist = [];
items.forEach((item) {
jsonlist.add(item.toJson());
});
return jsonEncodeSafe(jsonlist);
}
}
class User implements DataModel {
final String? name;
User({this.name});
#override
Map<String, dynamic> toJson() {
final Map<String, dynamic> map = <String, dynamic>{};
map['name'] = name;
return map;
}
}
void main() {
List<User> users = [User(name: 'User 1'), User(name: 'User 2')];
String usersJson = DataModelJsonHandler.itemsToJson();
log(usersJson);
List<User> usersFromJson = DataModelJsonHandler.itemsFromJson(usersJson); // Error
}

type 'JSArray<dynamic>' is not a subtype of type 'List<Ad>'

I tried two method to parse the rawData into dart objects. One using a for loop ads and it works but why _ads is not working when I use map ?
void main() {
dynamic rawData = [
{"title": "a", "id": 1}
];
List<Ad> ads = [];
for (var raw in rawData) {
Ad ad = Ad.fromJson(raw);
ads.add(ad);
}
print(ads);
List<Ad> _ads = rawData.map((e) => Ad.fromJson(e)).toList();
print(_ads);
}
class Ad {
Ad({
this.id,
this.title,
});
int id;
String title;
factory Ad.fromJson(Map<String, dynamic> json) => _$AdFromJson(json);
Map<String, dynamic> toJson() => _$AdToJson(this);
}
Ad _$AdFromJson(Map json) {
return Ad(
id: json['id'] as int,
title: json['title'] as String,
);
}
Map<String, dynamic> _$AdToJson(Ad instance) {
final val = <String, dynamic>{};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('id', instance.id);
writeNotNull('title', instance.title);
return val;
}
The result of calling nearly any method on a dynamic defined variable are going to be dynamic since the Dart compiler are going through a difficult time guessing the type you want. So when you want the result to be saved into a variable with a specific type like List<Ad> _ads you really need to tell the compiler at each step what generic type you want and expect.
With that said, you can get you code to work by changing:
List<Ad> _ads = rawData.map((e) => Ad.fromJson(e)).toList();
Into:
List<Ad> _ads = rawData.map<Ad>((e) => Ad.fromJson(e)).toList();
And if you also want to make the analyzer happy:
List<Ad> _ads = rawData.map<Ad>((Map<String, dynamic> e) => Ad.fromJson(e)).toList() as List<Ad>;

Flutter Sqflite error says The method 'query' was called on null

I am trying to fetch some data from network and store it in sqlite database. Following is the model class
class StudentModel {
int status;
String msg;
StudentModelData studentModelData;
StudentModel({this.status, this.msg, this.studentModelData});
StudentModel.fromJson(Map<String, dynamic> json) {
status = json['status'];
msg = json['msg'];
studentModelData = json['data'] != null ? new StudentModelData.fromJson(json['data']) : null;
}
StudentModel.fromDb(Map<String, dynamic> parsedJson) {
status = parsedJson['status'];
msg = parsedJson['msg'];
studentModelData = studentModelData = jsonDecode(json['data']) != null ? new StudentModelData.fromJson(jsonDecode(json['data'])) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['msg'] = this.msg;
if (this.studentModelData != null) {
data['data'] = this.studentModelData.toJson();
}
return data;
}
}
class StudentModelData {
int lastIndex;
List<StudentData> studentData;
StudentModelData({this.lastIndex, this.studentData});
StudentModelData.fromJson(Map<String, dynamic> json) {
lastIndex = json['lastIndex'];
if (json['studentData'] != null) {
studentData = new List<StudentData>();
json['studentData'].forEach((v) {
studentData.add(new StudentData.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['lastIndex'] = this.lastIndex;
if (this.studentData != null) {
data['studentData'] = this.studentData.map((v) => v.toJson()).toList();
}
return data;
}
}
class StudentData {
String studentId;
String studName;
String studProfilepic;
String studentEmail;
String studentMobile;
String courseName;
String classCode;
int minAvg;
int avg;
StudentData(
{this.studentId,
this.studName,
this.studProfilepic,
this.studentEmail,
this.studentMobile,
this.courseName,
this.classCode,
this.minAvg,
this.avg});
StudentData.fromJson(Map<String, dynamic> json) {
studentId = json['student_id'];
studName = json['stud_name'];
studProfilepic = json['stud_profilepic'];
studentEmail = json['student_email'];
studentMobile = json['student_mobile'];
courseName = json['course_name'];
classCode = json['class_code'];
minAvg = json['minAvg'];
avg = json['avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['student_id'] = this.studentId;
data['stud_name'] = this.studName;
data['stud_profilepic'] = this.studProfilepic;
data['student_email'] = this.studentEmail;
data['student_mobile'] = this.studentMobile;
data['course_name'] = this.courseName;
data['class_code'] = this.classCode;
data['minAvg'] = this.minAvg;
data['avg'] = this.avg;
return data;
}
}
And my database provider class looks like following
class StudentDbProvider implements Source, Cache {
Database db;
StudentDbProvider() {
init();
}
void init() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, "students.db");
db = await openDatabase(path, version: 1,
onCreate: (Database newDb, int version) {
newDb.execute("""
CREATE TABLE STUDENTS(
id INTEGER PRIMARY KEY,
status INTEGER,
msg TEXT,
data BLOB
)
""");
});
}
#override
Future<int> clear() {
return db.delete("STUDENTS");
}
#override
Future<StudentModel> fetchStudents(String disciplineId, String schoolId,
String year_id, String lastIndex) async {
final maps =
await db.query("STUDENTS");
if (maps.length > 0) {
return StudentModel.fromDb(maps.first);
}
return null;
}
#override
Future<int> addStudent(StudentModel studentModel) {
return db.insert("STUDENTS", studentModel.toJson(),conflictAlgorithm: ConflictAlgorithm.ignore);
}
}
final studentDbProvider = StudentDbProvider();
Whenever I tried to fetch the data and stored in the database, I get the following error in the console
NoSuchMethodError: The method 'query' was called on null.
Receiver: null
Tried calling: query("STUDENTS")
#0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
The data gets added to the database but I am not able to query the data from the database.
Reducing at minimum your example, this throws the exception The method 'query' was called on null
because fetch is executed before db is properly initialized:
class Database {
Future<int> query() {
return Future.value(1);
}
}
const oneSecond = Duration(seconds: 1);
class Provider {
Database db;
Provider() {
init();
}
void init() async {
db = await Future.delayed(oneSecond, () => Database());
}
Future<int> fetch() {
return db.query();
}
}
main() async {
var provider = Provider();
await provider.fetch();
}
The problem resides in calling an async method inside a constructor, see also:
Calling an async method from component constructor in Dart
This works:
class Database {
Future<int> query() {
return Future.value(1);
}
}
const oneSecond = Duration(seconds: 1);
class Provider {
Database db;
Provider() {
//init();
}
void init() async {
db = await Future.delayed(oneSecond, () => Database());
}
Future<int> fetch() {
return db.query();
}
}
main() async {
var provider = Provider();
await provider.init();
await provider.fetch();
}
Please note that init must be awaited, otherwise you will catch the same The method 'query' was called on null.
the problem is the init must be awaited. here what I did to fix it
_onCreate(Database db, int version) async {
await db.execute('CREATE TABLE ... <YOUR QUERY CREATION GOES HERE>');
}
Future<Database> getDatabaseInstance() async {
final String databasesPath = await getDatabasesPath();
final String path = join(databasesPath, '<YOUR DB NAME>');
return await openDatabase(path, version: 1, onCreate: _onCreate);
}
Future<int> save(Contact contact) {
return getDatabaseInstance().then((db) {
final Map<String, dynamic> contactMap = Map();
contactMap['name'] = contact.name;
contactMap['account_number'] = contact.accountNumber;
return db.insert('contacts', contactMap);
});
}
The SQFlite page gives a good example about it and helps a lot.
https://github.com/tekartik/sqflite/blob/master/sqflite/doc/opening_db.md

Dart Convert List as Map Entry for JSON Encoding

I asked a question before about Dart encoding/decoding to JSON, however, the libraries that were suggested were not complete and I decided to manually handle that.
The objective is to convert these objects to a map.
class Parent extends Object {
int id;
String name;
List<Child> listChild = new List<Child>();
Map toMap() => {"id":id, "name":name, "listChild":listChild};
}
class Child extends Object {
int id;
String childName;
Map toMap() => {"id":id, "childName":childName};
}
When doing
print(JSON.encode(parent.toMap()));
I am seeing it go here, any suggestion how to make this work?
if (!stringifyJsonValue(object)) {
checkCycle(object);
try {
var customJson = _toEncodable(object);
if (!stringifyJsonValue(customJson)) {
throw new JsonUnsupportedObjectError(object);
}
_removeSeen(object);
} catch (e) {
throw new JsonUnsupportedObjectError(object, cause : e);
}
}
}
Map toMap() => {"id":id, "name":name: "listChild": listChild.map((c) => c.toJson().toList())};
is valid for JSON.
import 'dart:convert' show JSON;
...
String json = JSON.encode(toMap());
You can also use the toEncodeable callback - see How to convert DateTime object to json
If your class structure does not contain's any inner class then follow
class Data{
String name;
String type;
Map<String, dynamic> toJson() => {
'name': name,
'type': type
};
}
If your class uses inner class structure
class QuestionTag {
String name;
List<SubTags> listSubTags;
Map<String, dynamic> toJson() => {
'name': name,
'listSubTags': listSubTags.map((tag) => tag.toJson()).toList()
};
}
class SubTags {
String tagName;
String tagDesc;
SubTags(this.tagName, this.tagDesc);
Map<String, dynamic> toJson() => {
'tagName': tagName,
'tagDesc': tagDesc,
};
}
Just rename Map toMap() into Map toJson() and it will work fine. =)
void encode() {
Parent p = new Parent();
Child c1 = new Child();
c1 ..id = 1 ..childName = "Alex";
Child c2 = new Child();
c2 ..id = 2 ..childName = "John";
Child c3 = new Child();
c3 ..id = 3 ..childName = "Jane";
p ..id = 1 ..name = "Lisa" ..listChild = [c1,c2,c3];
String json = JSON.encode(p);
print(json);
}
class Parent extends Object {
int id;
String name;
List<Child> listChild = new List<Child>();
Map toJson() => {"id":id, "name":name, "listChild":listChild};
}
class Child extends Object {
int id;
String childName;
Map toJson() => {"id":id, "childName":childName};
}

Resources