I am trying to set a property from an external object:
main() {
A a = A();
print(a.prop.runtimeType); // is 'Null'
MiddleMan().b.assign(a.prop);
print(a.prop.runtimeType); // should be 'B', but is 'Null'
}
class A { B prop; }
class MiddleMan { B b = B(); }
class B {
assign(B property) { property = this; }
}
I managed to achieve this functionality with mirrors:
import 'dart:mirrors';
main() {
A a = A();
print(a.prop.runtimeType); // is 'Null'
MiddleMan().b.assign(a, 'prop');
print(a.prop.runtimeType); // is 'B'
}
class A { B prop; }
class MiddleMan { B b = B(); }
class B {
assign(dynamic object, String property) {
InstanceMirror reflectee = reflect(object);
reflectee.setField(Symbol(property), this);
}
}
But then I realized mirrors are disabled in Flutter. Is there any workaround?
Just as mentioned here, one can use a wrapper class:
main() {
A a = new A();
print(a.prop.value.runtimeType); // is 'Null'
MiddleMan().b.assign(a.prop);
print(a.prop.value.runtimeType); // is 'B'
}
class A {
Wrapper<B> prop = Wrapper(null);
}
class MiddleMan {
B b = B();
}
class B {
assign(Wrapper<B> property) {
property.value = this;
}
}
class Wrapper<T> {
T value;
Wrapper(this.value);
}
But because my intention is to write a library and make its use easy for the user, with one line call such as:
MiddleMan().b.assign(a.prop);
And at the same time I needed to optionally process additional stuff when the property is assigned, so a direct assignment like a.prop = MiddleMan().b;, for now I decided to utilize somewhat unusual syntax with overloading the & operator, which results in usage as:
a.prop = MiddleMan().b;
or optionally:
a.prop = MiddleMan().b & doAdditionalStuff;
Here's the implementation:
class B {
B operator &(Function doAdditionalStuff) {
if (doAdditionalStuff != null)
doAdditionalStuff();
return this;
}
}
To give it more sense of what I am trying to achieve, my lib is supposed to work in context with the Provider library. User code example:
class MyStore with ChangeNotifier {
B prop;
MyStore() {
// MiddleMan is Singleton
prop = MiddleMan().b;
// or alternatively
prop = MiddleMan().b & notifyListeners;
}
}
Library code example:
class B {
List<Function> _reactives = [];
B operator &(Function notifyListenersCallback) {
if (notifyListenersCallback != null)
this._reactives.add(notifyListenersCallback);
return this;
}
// called on each internally triggered change
notifyListeners() {
for (Function reactive in this._reactives) {
if (reactive != null)
reactive();
}
}
}
Related
in my Flutter project, I want to decide class member type during execution. For this, I wrote the following code with the help of this entry: https://github.com/flutter/flutter/issues/17766 But I cannot access function printt(). It gives error: The method 'printt' isn't defined for the type 'Object'.
void main() {
Merchant merchant = Merchant('A');
merchant.mp!.printt(); // this line gives error: The method 'printt' isn't defined for the type 'Object'.
}
class Merchant {
Object? mp;
Merchant(String mpStr) {
if (mpStr == 'A') mp = createInstanceOf<A>();
if (mpStr == 'B') mp = createInstanceOf<B>();
}
T? createInstanceOf<T>() {
final factories = <Type, T Function()>{
A: () => A() as T,
B: () => B() as T,
};
final createInstance = factories[T];
return createInstance?.call(); // Same as createInstance() but null safety
}
}
class A {
void printt() {
print('I am function of class A');
}
}
class B {
void printt() {
print('I am function of class B');
}
}
Like #Yannis mentioned, create an abstract class, let's say PrintT, with all common methods of A and B and set mp as this type. Then, set both class A and B to implement it. The code is going to be the following:
Check out the live demo on DartPad.
void main() {
Merchant merchant = Merchant('A');
merchant.mp!.printt();
}
class Merchant {
PrintT? mp;
Merchant(String mpStr) {
if (mpStr == 'A') mp = createInstanceOf<A>();
if (mpStr == 'B') mp = createInstanceOf<B>();
}
T? createInstanceOf<T>() {
final factories = <Type, T Function()>{
A: () => A() as T,
B: () => B() as T,
};
final createInstance = factories[T];
return createInstance?.call(); // Same as createInstance() but null safety
}
}
abstract class PrintT {
void printt();
}
class A implements PrintT {
void printt() {
print('I am function of class A');
}
}
class B implements PrintT {
void printt() {
print('I am function of class B');
}
}
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)
Assume two unrelated classes (from Flutter libraries, etc):
class A {
String name;
}
class B {
int age;
}
Is it possible to have a List<A/B>? I know it's possible to have List<dynamic> but that would allow for Cs, Ds and Zs to be accepted as well.
You can create a parent abstract class for A and B and add a List which only allows children from the parent class.
abstract class Foo {
}
class A extends Foo {
}
class B extends Foo {
}
class C {
}
This is correct:
List<Foo> items = [A(), B()];
This isn't
List<Foo> items = [A(), B(),C()];
And you can identify the type with your own variable or using the runtimeType
for(Foo item in items){
print(item.runtimeType);
}
Another option (long version)
class Bar {
final A a;
final B b;
Bar({this.a, this.b}) {
if (a == null && b == null || a != null && b != null) throw ArgumentError("only one object is allowed");
}
}
class A {
}
class B {
}
class C {
}
Usage
List<Bar> items = [Bar(a: A()), Bar(b: B())];
for(Bar item in items){
if (item.a != null) print("Item : ${item.a}");
if (item.b != null) print("Item : ${item.b}");
}
I'm making a plugin for Aurelia and need a class decorator that
adds attributes to the new object instance, and
calls an external function with the new object as an argument.
I've looked through examples, and so far I've put together ("pseudo-ish" code)
return function addAndCall(target: any): any {
var original = target;
var newConstructor = function (...args) {
original.apply(this, args);
this.newAttribute = "object instance value";
ExternalModule.externalFunction(this);
};
newConstructor.prototype = Object.create(original.prototype);
newConstructor.prototype.constructor = original;
return <any>newConstructor;
}
but
I'm not entirely clear on the details here (or what is actually needed), and
it might not work properly since I'm getting Aurelia errors when using objects instantiated from classes with this decorator (and I suspect it's my decorator rather than the Aurelia framework that's buggy).
Any help and explanation would be greatly appreciated!
Why not just assign those properties to the prototype, and subsequently assign to the instance on first invocation
// decorator
function addAndCall(cb: Function, newField: string) {
// cb is now available in the decorator
return function(ctor: Function): void {
Object.defineProperty(ctor.prototype, newField, {
value: function(...args: any[]) {
return Object.defineProperty(this, newField, {
value: function(...args: any[]) {
console.log(newField, ...args);
}
})[newField](...args);
}
});
cb(ctor);
}
}
let callMe = (decoratedCtor) => console.log(decoratedCtor);
#addAndCall(callMe, 'propertyName')
class AddToMe {}
let addToMe = new AddToMe();
(<any>addToMe).propertyName(1, 2);
Here's a working version:
function addAndCall(target: any) {
var original = target;
function construct(constructor, args) {
var c: any = function () {
this.newAttribute = "object instance value";
ExternalModule.externalFunction(this);
return constructor.apply(this, args);;
}
c.prototype = constructor.prototype;
return new c();
}
var f: any = function (...args) {
return construct(original, args);
}
f.prototype = original.prototype;
return f;
}
(code in playground)
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
}
}