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

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.

Related

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
}

How to <T extend BaseClass> where BaseClass has a factory function?

Following is a simple class that provides a few helper functions for reading and writing data.
class BaseDAO<T> {
final String _modelName;
static late final StoreRef<int, Map<String, Object?>> _store;
BaseDAO(this._modelName) {
_store = intMapStoreFactory.store(_modelName);
}
Future<Database> get _db async => await AppDatabase().database;
Future<void> create(T object) async {
await _store.add(await _db, object.toJSON()); //The method 'toJSON' can't be unconditionally invoked because the receiver can be 'null'.
}
}
Now the issue with this is that the generic type T doesn't have toJSON function. I tried fixing this by writing an abstract class.
abstract class BaseModel {
Map<String, dynamic> toJSON();
factory BaseModel.fromJson(Map<String, dynamic> json);
}
and extending T with BaseModel. This presents all kinds of issues one of them being that I am unable to write an abstract class.
Any solution will be greatly appreciated.
In your case T object is empty. You need to extend it to some object which provides a method toMap().
Example:
class BaseDAO<T extends BaseModel> {
final String _modelName;
static late final StoreRef<int, Map<String, Object?>> _store;
BaseDAO(this._modelName) {
_store = intMapStoreFactory.store(_modelName);
}
Future<Database> get _db async => await AppDatabase().database;
Future<void> create(T object) async {
await _store.add(await _db, object.toMap()); // <- Dart see that this object extends to BaseModel and has a method `toMap()`,
}
}
abstract class BaseModel {
Map<String, dynamic> toMap();
}
class User extends BaseModel {
User({this.name});
final String? name;
#override
Map<String, dynamic> toMap() => {'name': name};
}
Future<void> create() async {
final user = User(name: 'Superman');
final base = BaseDAO('ModelName');
await base.create(user); // <- The user object will be added as `Map`.
}

How do I create an abstract factory?

Is it possible to somehow create an abstract factory method? Maybe what I'm trying to do is possible to implement differently?
abstract class ApiModel {
// Error: A function body must be provided.
factory ApiModel.fromJson(Map<String, dynamic> json);
}
class User extends ApiModel {
final int id;
final String name;
User({required this.id, required this.name});
#override
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
);
}
}
class ApiResponse<Model extends ApiModel> {
final List<Model> results;
ApiResponse({required this.results});
factory ApiResponse.fromJson(Map<String, dynamic> json) {
return ApiResponse(results: (json['results'] as List).map((item) => Model.fromJson(item)).toList());
}
}
I solved it like this:
factory ApiResponse.fromJson(Map<String, dynamic> json, Model Function(dynamic) mapper) {
return ApiResponse(
info: Info.fromJson(json['info']),
results: (json['results'] as List).map(mapper).toList(),
);
}

initializing class as model inside BlocBuilder and getting error

in my application i have model as this below structure and i want to initializing that inside BlocBuilder to update ui elements such as Text(), for example:
class _Login extends State<Login> {
UserInfo _userInfo = UserInfo();
LoginListingBloc loginListingBloc;
#override
void initState() {
loginListingBloc = BlocProvider.of<LoginListingBloc>(context);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder(
bloc: loginListingBloc,
builder: (BuildContext context, LoginListingStates state) {
if (state is LoginUninitializedState) {
} else if (state is LoginFetchedState) {
_userInfo = state.userInfo;
print(_userInfo.name);
}
return Text("My Username from server is: $_userInfo.name");
},
),
);
}
}
i don't want to make some variable such as name,email or avatart and fill them inside BlocBuilder to use them because i have this class model and i think i can initialize inside BlocBuilder to use that,
unfortunately i get this error when i run application:
The following ArgumentError was thrown building
BlocBuilder(dirty,
dependencies: [MediaQuery], state:
_BlocBuilderBaseState#2342d): Invalid argument(s)
LoginListingStates class content:
abstract class LoginListingStates {}
class LoginUninitializedState extends LoginListingStates {}
class LoginFetchingState extends LoginListingStates {}
class LoginFetchedState extends LoginListingStates {
final UserInfo userInfo;
LoginFetchedState({this.userInfo}) : assert(userInfo != null);
}
class LoginErrorState extends LoginListingStates {}
and my UserInfo model class:
class UserInfo {
String _name;
String _email;
String _avatar;
UserInfo();
UserInfo.fromJson(Map<String, dynamic> json)
: _name = json["name"],
_email = json["email"],
_avatar = json["avatar"],
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = _name;
data['email'] = _email;
data['avatar'] = _avatar;
return data;
}
String get email => _email;
String get name => _name;
int get avatar=> _avatar;
}

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