Search code examples
asynchronousfluttereventsdartobserver-pattern

How to use or create an Observer Pattern in Dart (specially for class properties)


I'm having trouble trying to find the standard way of using the Observer Pattern in Dart; almost everything I find is very outdated or with deprecated packages.

What I'm looking for is pretty close to this answer (outdated), in which @observable is used on top of a property, and then you can listen to changes to the property (very similar to Angular in TypeScript). A similar effect can be achieved with the ChangeNotifier class but it is inside one of the Flutter packages (foundation), so I don't think this is the standard way, otherwise it would be a simple Dart package.

I've also found 2 other neat packages that do offer something close to what I'm thinking about: property_change_notifier and simple_observable.

At any rate, I wonder if there is a simple recipe to add the Observer functionality to a class property. I'm thinking about something like turning the property (actually another getter based on the property) into a Stream, which would yield a new value every time the setter on that property was called. Or have the setter take also a Stream as a parameter and have it pass a value to the Stream once the property has been changed (is it possible to append a value to a Stream externally?).


Solution

  • You can use StreamView to make a class implement the stream interface – which is observable.

    Here's an example:

    class Counter extends StreamView<Counter> {
      Counter._(this._controller) : super(_controller.stream);
      factory Counter() => Counter._(StreamController());
    
      final StreamController<Counter> _controller;
    
      Future<void> close() => _controller.close();
    
      int _count = 0;
      int get count => _count;
    
      void increment() {
        _count++;
        _controller.add(this);
      }
    }
    

    Which allows you to do:

    void main() {
      final counter = Counter();
      counter.listen((value) {
        print(value.count);
      });
    }