15

I would like to implement an observer pattern in Dart but I'm not sure how to go about it.

Let's say I have a class:

class MyClass {

  String observed_field;

}

Now, whenever I change the field, I'd like to print "observed_field changed" string into the console. Pretty simple to do with a custom setter:

class MyClass {

  String _observed_field;

  get observed_field    => _observed_field;
  set observed_field(v) {
    _observed_field = v;
    print("observed_field changed");
  }

}

Now, of course, if I have not one, but many of those fields, I wouldn't want to create all those getters and setters. The obvious theoretical solution is to have them dynamically added to the class with something like this (not a working code, just an example of how I wish it looked):

class MyClass

  String  _observeable_field;
  String  _observeable_field_2;

  observe(#observeable_field, #observeable_field_2);

end

Is it even possible? Additionally, it would be super awesome to not have those fields defined above the observe() call, but rather write something like:

observe(String: #_observeable_field, String: #_observeable_field_2);

So that those fields are declared automatically.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
orion3
  • 9,797
  • 14
  • 67
  • 93

1 Answers1

19

Here's a way to do it using the Observe package. The example is taken from code comments in that package (and adapted to your example above). Essentially, you annotate fields you want to be observable with the @observable annotation, and then listen for changes (which you trigger with the call to Observable.dirtyCheck();

First, add the observable package in your pubspec.yaml

dependencies:
  observe: any

Then create a quick test program...

import 'package:observe/observe.dart';

class MyClass extends Object with Observable {
  @observable String observedField = "Hello";

  toString() => observedField.toString(); 
}

main() {
  var obj = new MyClass();

  // anonymous function that executes when there are changes
  obj.changes.listen((records) {
    print('Changes to $obj were: $records');
  });


  obj.observedField = "Hello World";

  // No changes are delivered until we check for them
  Observable.dirtyCheck();

  print('done!');
}

This produces the following output:

Changes to Hello World were: [#<PropertyChangeRecord Symbol("observedField") from: Hello to: Hello World>]
done!

Update in response to comments... Updating the example to omit the Observable.dirtyCheck() you can use a setter and notifyPropertyChanged, with the class instead mixing in ChangeNotifier

class MyClass2 extends Object with ChangeNotifier {

  String _observedField = "Hello";

  @reflectable get observedField    => _observedField;
  @reflectable set observedField(v) {
    _observedField = notifyPropertyChange(#observedField, _observedField, v);    
  }

  toString() => observedField;

}
Chris Buckett
  • 13,738
  • 5
  • 39
  • 46
  • I read another question on SO and it was suggest to use this `Observe` package. However I disliked that you actually had to call `Observable.dirtyCheck()` in order for the changes to be delivered. This sort of negates the whole point of observing something, if you still had to, basically, check for the changes manually. – orion3 Nov 25 '13 at 12:49
  • 1
    If you take a look at the example on the API docs, it shows how you can avoid the dirtyCheck() by using the @reflectable annotation and notifyPropertyChange(). This is more akin to your example. http://api.dartlang.org/docs/channels/stable/latest/observe.html – Chris Buckett Nov 25 '13 at 12:51
  • @ChrisBuckett I see you write **class MyClass extends Object with Observable** is there a difference to **class MyClass extends Observable** – H.R. Sep 14 '14 at 16:43
  • and I guess since the class is now observable we don't need to create them like Lists or Maps **var obj = toObservable(new MyClass())**?? – H.R. Sep 14 '14 at 16:48
  • `toObservable` is only for lists and maps, no need to call it on other objects. To your previous comment: `class MyClass extends Object with Observable` is a mixin application and `class MyClass extends Observable` is inheritance. The former is useful if you want or have to inherit from another class than `Observable`. In Dart you can't inherit from more than one class. Here mixins come in handy because they provide some kind of limited multiple inheritance. If you want to stick with one style you can just always use the mixin variant for `Observable` even when you just extend `Object`. – Günter Zöchbauer Sep 21 '14 at 11:35
  • There is no package as Observe _? – Cem Kaan Apr 10 '21 at 11:55