Passing Class to Function - dart

I’d like to pass the class User as second argument to the load-function.
Maybe someone knows how?
class App {
Map<String, List<Model>> models = {
'users': List<User>()
};
App() {
load('users');
}
void load(resource) {
Directory directory = Directory.fromUri(Uri(path: './app/data/' + resource));
for (FileSystemEntity file in directory.listSync(recursive: false)) {
Map<String, dynamic> fileContent = jsonDecode(File(file.uri.path).readAsStringSync());
models[resource].add(User.fromJson(fileContent));
}
}
}

You can't.
You can pass a Type object as argument, or a type as type argument (load<T>(resource) { ... }), but neither will allow you to call User.fromJson on the type.
What you can do is to pass the User.fromJson function as an argument (or, if that's a constructor, pass (x) => User.fromJson(x) as argument). That is:
void load(String resource, Object? fromJson(Map<String, dynamic> json)) {
...
map[resoruce].add(fromJson(fileContent));
}
and then
load("someName", (map) => User.fromJson(map));

Related

Is this factory a bad practice?

I'm working for a generic CRUD Repository but I have some problems with the serialization of my classes. For solving this issue I have create a factory on my base Entity:
const factories = {
Product: Product.fromJson,
};
abstract class Entity {
[...]
factory Entity.fromJson({
required Type type,
required Map json,
}) {
final factory = factories[type];
if (factory == null) {
throw Exception('Type Error: ($type) not found on factories');
}
return factory(json: json);
}
Map get json => {'id': id};
}
And in my repository abraction I just use this factory:
abstract class IDioCrudRepository<E extends Entity> extends DioRepository {
IDioCrudRepository({required super.baseUrl});
FutureOr<E> get(String id) async {
final res = await dio.get('$baseUrl/$id');
return Entity.fromJson(
type: E,
json: res.data as Map,
) as E;
}
[...]
}
Dou you know guys if this is a bad practice?
I would suggest you , at least, to make a Builder class and to move all that code far from Entity. In this manner entity class would not have any responsibility on how to build itself from a key and a json map.
const factories = {
Product: Product.fromJson,
};
class EntityBuilder {
static Entity fromJson({
required Type type,
required Map json,
}) {
final factory = factories[type];
if (factory == null) {
throw Exception('Type Error: ($type) not found on factories');
}
return factory(json: json);
}
}
abstract class Entity {
[...]
Map get json => {'id': id};
}
abstract class IDioCrudRepository<E extends Entity> extends DioRepository {
IDioCrudRepository({required super.baseUrl});
FutureOr<E> get(String id) async {
final res = await dio.get('$baseUrl/$id');
return EntityBuilder.fromJson(
type: E,
json: res.data as Map,
) as E;
}
[...]
}

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

How to create a base factory and override it on child class in Flutter

So I have a class like Question like bellow:
#JsonSerializable()
class Question {
String id;
String content;
Question({this.id, this.content});
factory Question.fromJson(Map<String, dynamic> json) =>
_$QuestionFromJson(json);
Map<String, dynamic> toJson() => _$QuestionToJson(this);
}
Please keep in mind that those _$QuestionFromJson and _$QuestionToJson comes from this library https://pub.dev/packages/json_serializable
Say I have many class like that which have a fromJson factory and a toJson method. I want to create a base class that contains those 2 method. A base model is easy for toJson as bellow:
abstract class BaseModel {
Map<String, dynamic> toJson();
}
But what about the factory method, I have no idea how to declare them then override it simply like:
#override
factory Question.fromJson(Map<String, dynamic> json) =>
_$QuestionFromJson(json);
EDIT:
My idea of using this is because I want to create a converter utility that I only need to pass in the class of the result like Converter.listFromJson<MyClass>(jsonString). For now, the helper is:
static List<T> listFromJson<T>(jsonString, Function mappingFunction) {
return myJsonMap.map(mappingFunction).cast<T>().toList();
}
so I have to map each item by passing the map function every time I use this helper method:
Converter.listFromJson<Question>(
jsonMap, (item) => Question.fromJson(item));
There'are a few more class that needs to be convert to the list like this. I want to reuse the method without the (item) => Question.fromJson(item) method part. That's why I want to create a base class that have the factory fromJson method so that I can use it in the converter
return myJsonMap.map((item) => BaseModel.fromJson(item)).cast<T>().toList();
then I just simply call
Converter.listFromJson<Question>(jsonMap);
Thank you for your time.
i don't know if i got you correctly, that's what i understood from your question
abstract class BaseModel{
BaseModel();
BaseModel.fromJson(Map<String,dynamic> json);
}
class Question extends BaseModel{
final String id;
final String name;
Question({this.id,this.name}): super();
#override
factory Question.fromJson(Map<String, dynamic> json) {
return Question(
id: json['id'],
name: json['name']
);
}
}
void main(){
Map<String,dynamic> json = {'id': "dsajdas",'name': 'test'};
Question question = Question.fromJson(json);
print('question: ${question.id}');
}
That was my approach but you can't do such a thing. There is a workaround by declaring .fromJson(json) in a variable. Look at my sample codes, hope you can get an idea.
class Categories {
final String id;
String name;
String image;
Categories({this.id, this.name, this.image});
Categories.fromJson(dynamic json)
: id = json['id'],
name = json['name'],
image = json['image'];
}
class CategoriesModel extends AppModel<Categories> {
List<Categories> list = [];
Function fromJson = (dynamic json) => Categories.fromJson(json);
}
class AppModel<T> {
List<T> list = [];
Function fromJson;
List<T> getList() {
if (this.list.isNotEmpty) return this.list;
List<dynamic> list = GetStorage().read('tableName');
list.forEach((data) {
this.list.add(fromJson(data));
});
return this.list;
}
}

dart reflection call by String

I want to wrote method which call all function in class:
class Example extends MyAbstractClass {
void f1(){...}
void f2(){...}
void f3(){...}
Example(){
callAll();//this call f1(), f2() and f3().
}
}
I have problem in this part of code:
reflectClass(this.runtimeType).declarations.forEach((Symbol s, DeclarationMirror d) {
if (d.toString().startsWith("MethodMirror on ")) {
String methodName = d.toString().substring(16).replaceAll("'", "");
print(methodName);
// How to call function by name methodName?
}
});
instead of
if (d.toString().startsWith("MethodMirror on ")) {
you can use
if (d is MethodMirror) {
You need an InstanceMirror of an instance of the class. I think in your case this would be
var im = reflect(this).invoke(d.simpleName, []);
im.declarations.forEach((d) ...
see also How can I use Reflection (Mirrors) to access the method names in a Dart Class?
Using dson you can do:
library example_lib;
import 'package:dson/dson.dart';
part 'main.g.dart';
#serializable
class Example extends _$ExampleSerializable {
Example() {
_callAll();
}
fn1() => print('fn1');
fn2() => print('fn2');
fn3() => print('fn3');
fn4() => print('fn4');
_callAll() {
reflect(this).methods.forEach((name, method) {
if(name != '_callAll') this[name]();
});
}
}
main(List<String> arguments) {
_initMirrors();
new Example();
}

Obscure Dart syntax

While working through the the Dart Route library example for client side coding I came across this snippet.
var router = new Router()
..addHandler(urls.one, showOne)
..addHandler(urls.two, showTwo)
..addHandler(urls.home, (_) => null)
..listen();
My question is how does (_) => null work? It seems to specify a function that returns a null value but what does (_) mean?
(_) means it is a function with one parameter but you don't care about that parameter, so it's just named _. You could also write (ignoreMe) => null. The important thing here is, that there needs to be a function that accepts one parameter. What you do with it, is your thing.
(_) => null means : a function that take one parameter named _ and returning null. It could be seen as a shortcut for (iDontCareVariable) => null.
A similar function with no parameter would be () => null.
A similar function with more parameters would be (_, __, ___) => null.
Note that _ is not a special syntax defined at langauge level. It is just a variable name that can be used inside the function body. As example : (_) => _.
I will try explain this by the example.
void main() {
var helloFromTokyo = (name) => 'こんにちわ $name';
var greet = new Greet();
greet.addGreet('London', helloFromLondon)
..addGreet('Tokyo', helloFromTokyo)
..addGreet('Berlin', helloFromBerlin)
..addGreet('Mars', (_) => null)
..addGreet('Me', (name) => 'Privet, chuvak! You name is $name?')
..addGreet('Moon', null);
greet.greet('Vasya Pupkin');
}
String helloFromLondon(String name) {
return 'Hello, $name';
}
String helloFromBerlin(String name) {
return 'Guten tag, $name';
}
class Greet {
Map<String, Function> greets = new Map<String, Function>();
Greet addGreet(String whence, String sayHello(String name)) {
greets[whence] = sayHello;
return this;
}
void greet(String name) {
for(var whence in greets.keys) {
var action = greets[whence];
if(action == null) {
print('From $whence: no reaction');
} else {
var result = action(name);
if(result == null) {
print('From $whence: silent');
} else {
print('From $whence: $result');
}
}
}
}
}
Output:
From London: Hello, Vasya Pupkin
From Tokyo: こんにちわ Vasya Pupkin
From Berlin: Guten tag, Vasya Pupkin
From Mars: silent
From Me: Privet, chuvak! You name is Vasya Pupkin?
From Moon: no reaction

Resources