Replace getter and setter blocks with a oneliner when using ChangeNotifier.

When dart became ‘non-nullable by default’, an interesting keyword was added to the dart language: ‘late’.

Although it might not be so obvious at first glance, this keyword makes it possible to simplify getter and setter blocks when using a ChangeNotifier to update values on the screen.

Normally a getter and setter block does look like this.

int _counter = 0;

int get counter => _counter;

set counter(int value) {
  if (_counter != value) {
    _counter = value;
    notifyListeners();
  }
}

This code is not complicated. It consists of:

  • a private backing variable
  • a public getter and setter
  • some logic that tests if the value has changed and calls ‘notifyListeners’ when needed

There are also some drawbacks:

  • You can access the backing variable directly (by accident).
  • It is easy to make mistakes like checking or assigning the wrong backing variable, especially when you have more than 3 pairs of getters and setters.
  • The code is boring (error-prone) and takes up too much space.
  • Don’t repeat yourself (DRY) principle is not respected.

Now with the ‘late’ keyword, we can replace the code above with this one-liner:

late final counter = Property(0, notifyListeners);

Without the ‘late’ keyword this would not have been possible, because it is not allowed to access member functions (in this case notifyListeners) in an initializer. With the late keyword, initialization is deferred until the property is first referenced.

The implementation of the Property class is simple and solves all drawbacks of a regular getter and setter block.

class Property<T> {
  Property(T initialValue, this.notifyListeners) {
    _value = initialValue;
  }

  late T _value;
  final void Function() notifyListeners;

  T get value => _value;

  set value(T value) {
    if (_value != value) {
      _value = value;
      notifyListeners();
    }
  }
}

This makes it possible to reduce the business logic of the default Flutter counter app to these few lines:

class MainController extends ChangeNotifier {
  late final counter = Property<int>(0, notifyListeners);

  void incrementCounter() => counter.value++;
}

Source code to test this out can be found here:
https://github.com/jsroest/property_for_changenotifier