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?


Solution 1: Kaspi

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