I am trying to get all the subclasses of a given class following this post Find all subclasses in dart like this:
import 'dart:mirrors';
class A {}
class B extends A{}
List<Type> getSubclasses(Type type) {
List<Type> subClasses = [];
MirrorSystem mirrorSystem = currentMirrorSystem();
// NoSuchMethodError: Class '_LocalLibraryMirror' has no instance getter 'classes'. ???
mirrorSystem.isolate.rootLibrary.classes.forEach((s, c) {
if (c.superclass == type) {
subClasses.add(c);
}
});
return subClasses;
}
main() {
var result = getSubclasses(A);
print(result);
}
But I am getting the following error:
Unhandled exception:
NoSuchMethodError: Class '_LocalLibraryMirror' has no instance getter 'classes'.
Receiver: Instance of '_LocalLibraryMirror'
Tried calling: classes
The dart team probably removed that classes form the LibraryMirror, Does anybody knows any alternative?
I am using dart version: 1.24.3
Following Randal's suggestion, I could fix my problem with the following code:
import 'dart:mirrors';
List<ClassMirror> findSubClasses(Type type) {
ClassMirror classMirror = reflectClass(type);
return currentMirrorSystem()
.libraries
.values
.expand((lib) => lib.declarations.values)
.where((lib) {
return lib is ClassMirror &&
lib.isSubclassOf(classMirror) &&
lib != classMirror;
}).toList();
}
Hopefully this can help someone
Related
I couldn't figure out how to store functions while keeping the storage agnostic about the functions' parameter type. I try to implement a json registry used for encoding/decoding.
Decoding works fine but when it comes to encoding I get this error on runtime
E/flutter ( 4637): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: type '(QuotationRequest) => Map<String, dynamic>' is not a subtype of type '(Object) => Map<String, dynamic>'
E/flutter ( 4637): #0 JsonifyRegistry.encode (package:ridecube/src/commons/io/jsonify.dart:31:21)
...
when I call this:
final registry = JsonifyRegistry()
..register<QuotationRequest>(encoder: (_) => _.toJson(), decoder: (_) => QuotationRequest.fromJson(_));
final QuotationRequest data = QuotationRequest();
registry.encode(data); // <-- issue here
Hence, I'm curious how to do proper casting when it comes to dart function objects if possible.
// jsonify.dart
typedef Json = Map<String, dynamic>;
typedef JsonifyEncoder<T> = Json Function(T object);
typedef JsonifyDecoder<T> = T Function(Json encoded);
class JsonifyRegistryEntry<T> {
JsonifyRegistryEntry({required this.encoder, required this.decoder});
final JsonifyEncoder<T> encoder;
final JsonifyDecoder<T> decoder;
}
class JsonifyRegistry {
final Map<Type, JsonifyRegistryEntry<Object>> _reg = {};
void register<T>({required JsonifyEncoder<T> encoder, required JsonifyDecoder<T> decoder}) =>
registerEntry(JsonifyRegistryEntry<T>(encoder: encoder, decoder: decoder));
void registerEntry<T>(JsonifyRegistryEntry<T> entry) {
_reg[T] = entry as JsonifyRegistryEntry<Object>;
}
T decode<T>(Json encoded) {
assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
return _reg[T]!.decoder(encoded) as T;
}
Json encode<T>(T object) {
assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
return _reg[T]!.encoder(object!); //line 31
}
}
Note
I know why this is prevented of course but I'm still trying to find a solution for my implementation.
simple explanation:
typedef Fu1 = int Function(Object);
typedef Fu2 = int Function(int);
void main() {
final Fu2 fu2 = (x) => x;
final Fu1 fu1 = fu2 as Fu1; // error here Closure 'main_closure': type '(int) => int' is not a subtype of type '(Object) => int'
// if this wasn't the case you would get
// an error as soon as you make a call like
// this: fu1('string');
}
this is one possible solution but I don't like that I had to replace the property encoder by a method.. Maybe you guys can come up with a better advice.
class JsonifyRegistryEntry<T> {
JsonifyRegistryEntry({required JsonifyEncoder<T> encoder, required this.decoder}): _encoder = encoder;
final Function _encoder;
JsonifyEncoder<E> encoder<E>() => _encoder as JsonifyEncoder<E>;
final JsonifyDecoder<T> decoder;
}
// in JsonifyRegistry:
Json encode<T>(T object) {
assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
return _reg[T]!.encoder<T>()(object!);
}
I am modelling a Dart class with the new null safety types in mind. I believe there are two effective ways to initialize non-nullable properties, calculated from a parameter.
For this example, we will use the Favourite class.
This class uses the initializer list in the constructor.
class Favourite {
int favouriteId;
Favourite({required this.favouriteId});
Favourite.mapFromJson(dynamic json)
: this.favouriteId = json["favouriteId"];
}
This class uses the 'late' keyword.
class Favourite {
late int favouriteId;
Favourite({required this.favouriteId});
Favourite.mapFromJson(dynamic json) {
this.favouriteId = json["favouriteId"];
}
}
When would you use one over the other? Using 'late' feels risky. If I added another named constructor, the compiler would not complain about 'favouriteId' not being initialized.
Are there other options?
Thank you!
Neither.
Use a default constructor that initializes the fields themselves and a factory constructor that handles deserializing the json object:
class Favourite {
final int favouriteId;
Favourite({required this.favouriteId});
factory Favourite.fromMap(Map<String, dynamic> map) {
final favouriteId = json['favouriteId'];
assert(favouriteId != null && favouriteId is int);
return Favourite(
favouriteId: favouriteId,
);
}
}
The late keyword can be a source of headache if you don't handle it properly, so in general don't use it unless you have to.
If you're sure the json will always have a "favouriteId", you can write it like this:
class Favourite {
int favouriteId;
Favourite({required this.favouriteId});
Favourite.mapFromJson(Map<String, dynamic?> json):
assert(() {
final favouriteId = json["favouriteId"];
return favouriteId != null && favouriteId is int;
}()),
favouriteId = json["favouriteId"] as int;
}
void main() {
dynamic m = {"favouriteId":2};
final favourite = Favourite.mapFromJson(m);
print("favourite id: ${favourite.favouriteId}");
}
There are several models have a same structure, { type: xxx, settings: xxx}, so i would like to use a parent class "WidgetConfig" with a generic type "T" to implement this, but problem occurs when i add "fromJson" methods. How can i invoke method on a generic type or any other ways to implement this?
class BannerWidgetViewModel extends ChangeNotifier {
WidgetConfig<BannerWidgetConfig> config;
BannerWidgetViewModel(String configJson){
config = WidgetConfig.fromJson(configJson);
}
}
class BannerWidgetConfig {
String imgUrl;
String padImgUrl;
String lessonId;
BannerWidgetConfig.fromJson(json){
if (json != null) {
this.imgUrl = json['imgUrl'];
this.padImgUrl = json['padImgUrl'];
this.lessonId = json['lessonId'];
}
}
}
class WidgetConfig<T> {
WidgetType type;
WidgetConfig.fromJson(json){
if (json != null) {
this.type = json['type'];
// this.settings = T.fromJson(json['settings']); // T doesn't have fromJson method
}
}
}
then i use a abstract class but still not working.
abstract class BaseWidgetConfig {
BaseWidgetConfig.fromJson(dynamic json);
}
class WidgetConfig<T extends BaseWidgetConfig> {
WidgetType type;
T settings;
WidgetConfig.fromJson(json){
if (json != null) {
this.type = json['type'];
this.settings = T.fromJson();
}
}
}
code picture
Directly show the function as a reference.
send function as a reference here.
FireStoreHelper.getList<ModelLesson>('grade4', ModelLesson.fromJson);
get the method here.
static Future<List<T>> getList<T>(String path, Function fromJson)
Consider the following function:
BasicClass copyWith({
String id,
}) {
// some code behaving differently for 1) id is undefined and 2) id is explicit null
}
And consider the two parameters below:
Nothing (id is undefined)
copyWith();
Null (id is null)
copyWith(id: null);
in the copyWith method, is there any way I can make it behave differently for 1) and 2)
There is no way to differentiate null from "no parameter passed".
The only workaround (which is used by Freezed to generate a copyWith that supports null) is to cheat using a custom default value:
final undefined = Object();
class Example {
Example({this.param});
final String param;
Example copyWith({Object param = undefined}) {
return Example(
param: param == undefined ? this.param : param as String,
);
}
}
This requires typing your variables as Object though.
To fix that issue, you can use inheritance to hide the Object under a type-safe interface (again, see Freezed):
final undefined = Object();
class Example {
Example._();
factory Example({String param}) = _Example;
String get param;
void method() {
print('$param');
}
Example copyWith({String param});
}
class _Example extends Example {
_Example({this.param}): super._();
final String param;
#override
Example copyWith({Object param = undefined}) {
return Example(
param: param == undefined ? this.param : param as String,
);
}
}
If so, how is this accomplished? If not, are there any plans to support this in future Dart releases? I'm mostly referring to your own created custom annotations.
In this documentation link, https://www.dartlang.org/docs/spec/latest/dart-language-specification.html#h.d0rowtffuudf, it says: "Metadata is associated with the abstract syntax tree of the program construct p that immediately follows the metadata, assuming p is not itself metadata or a comment . Metadata can be retrieved at runtime via a reflective call, provided the annotated program construct p is accessible via reflection.
Reflective access to metadata is not yet implemented as of the M3 release."
Thank you.
Sample code for understanding.
import "dart:mirrors";
void main() {
var object = new Class1();
var classMirror = reflectClass(object.runtimeType);
// Retrieve 'HelloMetadata' for 'object'
HelloMetadata hello = getAnnotation(classMirror, HelloMetadata);
print("'HelloMetadata' for object: $hello");
// Retrieve 'Goodbye' for 'object.method'
var methodMirror = (reflect(object.method) as ClosureMirror).function;
Goodbye goodbye = getAnnotation(methodMirror, Goodbye);
print("'Goodbye' for object: $goodbye");
// Retrieve all 'Goodbye' for 'object.method'
List<Goodbye> goodbyes = getAnnotations(methodMirror, Goodbye);
print("'Goodbye's for object.method': $goodbyes");
// Retrieve all metadata for 'object.method'
List all = getAnnotations(methodMirror);
print("'Metadata for object.method': $all");
}
Object getAnnotation(DeclarationMirror declaration, Type annotation) {
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (reflectee.runtimeType == annotation) {
return reflectee;
}
}
}
return null;
}
List getAnnotations(DeclarationMirror declaration, [Type annotation]) {
var result = [];
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (annotation == null) {
result.add(reflectee);
} else if (reflectee.runtimeType == annotation) {
result.add(reflectee);
}
}
}
return result;
}
#HelloMetadata("Class1")
class Class1 {
#HelloMetadata("method")
#Goodbye("method")
#Goodbye("Class1")
void method() {
}
}
class HelloMetadata {
final String text;
const HelloMetadata(this.text);
String toString() => "Hello '$text'";
}
class Goodbye {
final String text;
const Goodbye(this.text);
String toString() => "Goodbye '$text'";
}
Output:
'HelloMetadata' for object: Hello 'Class1'
'Goodbye' for object: Goodbye 'method'
'Goodbye's for object.method': [Goodbye 'method', Goodbye 'Class1']
'Metadata for object.method': [Hello 'method', Goodbye 'method', Goodbye 'Class1']
P.S.
If Dart had supported the generic methods that I would recommend to use this code.
T getAnnotation<T>(DeclarationMirror declaration) {
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (reflectee.runtimeType == T) {
return reflectee;
}
}
}
return null;
}
And retrieve metadata with generic method.
var goodbye = getAnnotation<Goodbye>(methodMirror);
Yes you can retrieve annotations with dart:mirrors :
import 'dart:mirrors';
#override
class A {}
main(){
TypeMirror typeOfA = reflectType(A);
// or reflectType(a.runtimeType) if a is an instance of A
// getting metadata of the class
List<InstanceMirror> metadatas = typeOfA.metadata;
for (InstanceMirror m in metadatas) {
ClassMirror cm = m.type;
// here you get the Class of the annotation
}
}