How to define a factory method in dart abstracts - dart

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
}

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');
}
}
}

pass initial data into a singleton factory in dart

I would like to pass some initial information into a singleton in dart.
Unfortunately, the information I like to access is null (see dartpad output below)
It seems like I get a new instance of my object and not the singleton but I can not wrap my head around it. Any idea?
ElmCommandProvider.fromMetaData
ElmCommandProvider._internal()
ElmCommandProvider._init
ElmCommandProvider()
null
This is the code which can be pasted in DartPad
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
ElmCommandProvider._init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
ElmCommandProvider._init() {
print('ElmCommandProvider._init');
commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}
_init() should not be a constructor. Or at least there is no need for it to be one and it's confusing you. It should be changed to a static method or a private instance method.
When you do commandsList= in ElmCommandProvider._init(), commandsList is referring to the commandsList instance variable in the new ElmCommandProvider object you're creating with the constructor. You likely actually mean to modify the singleton's commandsList so you should have been doing singleton.commandsList = instead of just commandsList =.
Example working code with static method:
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
_init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
static _init() {
print('ElmCommandProvider._init');
_singleton.commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}
Example working code with private instance method:
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
_singleton._init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
void _init() {
print('ElmCommandProvider._init');
commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}

What is an equivalent for Dart 2 to `typeof` of TypeScript?

I'm new to Dart 2. I want a class to have a property. It's a reference of other class. it's not an instance but class itself. In TypeScript, it's possible to write as below. Is there a same way in Dart 2?
class Item { }
class ItemList {
itemClass: typeof Item;
}
const itemList = new ItemList();
itemList.itemClass = Item;
UPDATED:
I added some more context. The following is minimal sample code. I want to delegate a role of instantiation to super class.
class RecordBase {
id = Math.random();
toJson() {
return { "id": this.id };
};
}
class DbBase {
recordClass: typeof RecordBase;
create() {
const record = new this.recordClass();
const json = record.toJson();
console.log(json);
}
}
class CategoryRecord extends RecordBase {
toJson() {
return { "category": "xxxx", ...super.toJson() };
};
}
class TagRecord extends RecordBase {
toJson() {
return { "tag": "yyyy", ...super.toJson() };
};
}
class CategoryDb extends DbBase {
recordClass = CategoryRecord;
}
class TagDb extends DbBase {
recordClass = TagRecord;
}
const categoryDb = new CategoryDb();
categoryDb.create();
const tagDb = new TagDb();
tagDb.create();
I have tried to make you sample code into Dart. As I told before, you cannot get a reference to a class and call the constructor on runtime based on this reference.
But you can make a reference to a method which constructs the object of you class.
import 'dart:math';
class RecordBase {
static final Random _rnd = Random();
final int id = _rnd.nextInt(100000);
Map<String, dynamic> toJson() => <String, dynamic>{'id': id};
}
abstract class DbBase {
final RecordBase Function() getRecordClass;
RecordBase record;
Map<String, dynamic> json;
DbBase(this.getRecordClass);
void create() {
record = getRecordClass();
json = record.toJson();
print(json);
}
}
class CategoryRecord extends RecordBase {
#override
Map<String, dynamic> toJson() {
return <String, dynamic>{'category': 'xxxx', ...super.toJson()};
}
}
class TagRecord extends RecordBase {
#override
Map<String, dynamic> toJson() {
return <String, dynamic>{'tag': 'yyyy', ...super.toJson()};
}
}
class CategoryDb extends DbBase {
CategoryDb() : super(() => CategoryRecord());
}
class TagDb extends DbBase {
TagDb() : super(() => TagRecord());
}
void main() {
final categoryDb = CategoryDb();
categoryDb.create(); // {category: xxxx, id: 42369}
final tagDb = TagDb();
tagDb.create(); // {tag: yyyy, id: 97809}
}
I am not really sure if the create() method should be seen as a method or a constructor. So I choose to make it a method to be closer to your code.

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

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';
}

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

Resources