flutter监听变化通知Listenable、ChangeNotifier、ValueListenable

1,018 阅读3分钟

Listenable(抽象类)

abstract class Listenable {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const Listenable();

  /// Return a [Listenable] that triggers when any of the given [Listenable]s
  /// themselves trigger.
  ///
  /// The list must not be changed after this method has been called. Doing so
  /// will lead to memory leaks or exceptions.
  ///
  /// The list may contain nulls; they are ignored.
  factory Listenable.merge(List<Listenable?> listenables) = _MergingListenable;

  /// Register a closure to be called when the object notifies its listeners.
  void addListener(VoidCallback listener);

  /// Remove a previously registered closure from the list of closures that the
  /// object notifies.
  void removeListener(VoidCallback listener);
}

官方文档解释, Listenable是一个维护监听器列表的对象。监听器通常用于通知客户端对象已被更新。

使用 Listenable 的主要目的是为了实现响应式编程,让 UI 在数据变化时自动更新。

Listenable提供一个抽象构造函数。这个构造函数使子类能够提供const构造函数,以便它们可以在const表达式中使用。

addListener注册一个闭包,当对象通知侦听器时调用。

removeListener从该对象通知的闭包列表中删除先前注册的闭包。

Listenable.merge返回一个[Listenable],当任何给定的[Listenable]本身被触发时触发。 在调用此方法之后,不能更改列表。这样做将导致内存泄漏或异常。列表可以包含空值;他们被忽视了。

ValueListenable

abstract class ValueListenable<T> extends Listenable {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const ValueListenable();

  /// The current value of the object. When the value changes, the callbacks
  /// registered with [addListener] will be invoked.
  T get value;
}

Listenable的子类提供了一个接口, 公开了一个泛型Tvalue

这个接口由ValueNotifier<T>Animation<T>实现

ChangeNotifier

一个可以扩展或混合的类,它提供了一个使用[VoidCallback]通知的更改通知API。 添加侦听器是O(1),删除侦听器和分派通知是O(N)(其中N是侦听器的数量)。

mixin class ChangeNotifier implements Listenable {
int _count = 0;

// The _listeners is intentionally set to a fixed-length _GrowableList instead
// of const [].
//
// The const [] creates an instance of _ImmutableList which would be
// different from fixed-length _GrowableList used elsewhere in this class.
// keeping runtime type the same during the lifetime of this class lets the
// compiler to infer concrete type for this property, and thus improves
// performance.
static final List<VoidCallback?> _emptyListeners = List<VoidCallback?>.filled(0, null);
List<VoidCallback?> _listeners = _emptyListeners;
}

为了提升性能, 声明了一个_emptyListeners空数组。

_listeners变量赋初始值空数组_listeners

addListener

void addListener(VoidCallback listener) {
  assert(ChangeNotifier.debugAssertNotDisposed(this));
  if (kFlutterMemoryAllocationsEnabled && !_creationDispatched) {
    MemoryAllocations.instance.dispatchObjectCreated(
      library: _flutterFoundationLibrary,
      className: '$ChangeNotifier',
      object: this,
    );
    _creationDispatched = true;
  }
  if (_count == _listeners.length) {
    if (_count == 0) {
      _listeners = List<VoidCallback?>.filled(1, null);
    } else {
      final List<VoidCallback?> newListeners =
          List<VoidCallback?>.filled(_listeners.length * 2, null);
      for (int i = 0; i < _count; i++) {
        newListeners[i] = _listeners[i];
      }
      _listeners = newListeners;
    }
  }
  _listeners[_count++] = listener;
}

添加监听器, 当监听器的数量跟当前数组长度相等时,对数组扩容。确保有足够的空间存储监听器。

然后变量_count在递增。

_removeAt

void _removeAt(int index) {
  // The list holding the listeners is not growable for performances reasons.
  // We still want to shrink this list if a lot of listeners have been added
  // and then removed outside a notifyListeners iteration.
  // We do this only when the real number of listeners is half the length
  // of our list.
  _count -= 1;
  if (_count * 2 <= _listeners.length) {
    final List<VoidCallback?> newListeners = List<VoidCallback?>.filled(_count, null);

    // Listeners before the index are at the same place.
    for (int i = 0; i < index; i++) {
      newListeners[i] = _listeners[i];
    }

    // Listeners after the index move towards the start of the list.
    for (int i = index; i < _count; i++) {
      newListeners[i] = _listeners[i + 1];
    }

    _listeners = newListeners;
  } else {
    // When there are more listeners than half the length of the list, we only
    // shift our listeners, so that we avoid to reallocate memory for the
    // whole list.
    for (int i = index; i < _count; i++) {
      _listeners[i] = _listeners[i + 1];
    }
    _listeners[_count] = null;
  }
}

根据索引移除监听器。并根据监听器数量是否足够数组一半来选择是否进行数组的缩小。

removeListener

void removeListener(VoidCallback listener) {
  // This method is allowed to be called on disposed instances for usability
  // reasons. Due to how our frame scheduling logic between render objects and
  // overlays, it is common that the owner of this instance would be disposed a
  // frame earlier than the listeners. Allowing calls to this method after it
  // is disposed makes it easier for listeners to properly clean up.
  for (int i = 0; i < _count; i++) {
    final VoidCallback? listenerAtIndex = _listeners[i];
    if (listenerAtIndex == listener) {
      if (_notificationCallStackDepth > 0) {
        // We don't resize the list during notifyListeners iterations
        // but we set to null, the listeners we want to remove. We will
        // effectively resize the list at the end of all notifyListeners
        // iterations.
        _listeners[i] = null;
        _reentrantlyRemovedListeners++;
      } else {
        // When we are outside the notifyListeners iterations we can
        // effectively shrink the list.
        _removeAt(i);
      }
      break;
    }
  }
}

从对象更改时通知的闭包列表中删除先前注册的闭包。

如果给定的监听器没有注册,调用将被忽略。

如果[dispose]被调用,该方法立即返回。

notifyListeners

void notifyListeners() {
  assert(ChangeNotifier.debugAssertNotDisposed(this));
  if (_count == 0) {
    return;
  }

  // To make sure that listeners removed during this iteration are not called,
  // we set them to null, but we don't shrink the list right away.
  // By doing this, we can continue to iterate on our list until it reaches
  // the last listener added before the call to this method.

  // To allow potential listeners to recursively call notifyListener, we track
  // the number of times this method is called in _notificationCallStackDepth.
  // Once every recursive iteration is finished (i.e. when _notificationCallStackDepth == 0),
  // we can safely shrink our list so that it will only contain not null
  // listeners.

  _notificationCallStackDepth++;

  final int end = _count;
  for (int i = 0; i < end; i++) {
    try {
      _listeners[i]?.call();
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'foundation library',
        context: ErrorDescription('while dispatching notifications for $runtimeType'),
        informationCollector: () => <DiagnosticsNode>[
          DiagnosticsProperty<ChangeNotifier>(
            'The $runtimeType sending notification was',
            this,
            style: DiagnosticsTreeStyle.errorProperty,
          ),
        ],
      ));
    }
  }

  _notificationCallStackDepth--;

  if (_notificationCallStackDepth == 0 && _reentrantlyRemovedListeners > 0) {
    // We really remove the listeners when all notifications are done.
    final int newLength = _count - _reentrantlyRemovedListeners;
    if (newLength * 2 <= _listeners.length) {
      // As in _removeAt, we only shrink the list when the real number of
      // listeners is half the length of our list.
      final List<VoidCallback?> newListeners = List<VoidCallback?>.filled(newLength, null);

      int newIndex = 0;
      for (int i = 0; i < _count; i++) {
        final VoidCallback? listener = _listeners[i];
        if (listener != null) {
          newListeners[newIndex++] = listener;
        }
      }

      _listeners = newListeners;
    } else {
      // Otherwise we put all the null references at the end.
      for (int i = 0; i < newLength; i += 1) {
        if (_listeners[i] == null) {
          // We swap this item with the next not null item.
          int swapIndex = i + 1;
          while (_listeners[swapIndex] == null) {
            swapIndex += 1;
          }
          _listeners[i] = _listeners[swapIndex];
          _listeners[swapIndex] = null;
        }
      }
    }

    _reentrantlyRemovedListeners = 0;
    _count = newLength;
  }
}

调用所有已注册的监听器。每当对象更改时,调用此方法, 用来通知客户端对象可能已经发生了更改。

参考资料

Flutter 知识集锦 | 监听与通知 ChangeNotifier

Flutter 组件集录 | InheritedNotifier 内置状态管理组件