defaultNotifyListeners method

  1. @protected
  2. @visibleForTesting
void defaultNotifyListeners(
  1. Object? event
)

Notify all the listeners.

  • event The event to send to the listeners.

Implementation

@protected
@visibleForTesting
@pragma('vm:notify-debugger-on-exception')
void defaultNotifyListeners(Object? event) {
  assert(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 {
      final ChangeListener? listener = _listeners[i];
      if (listener != null) {
        listener.dispatch(event);
      }
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: _kDataWidgetLibrary,
        context: ErrorDescription(
            'while dispatching notifications for $runtimeType'),
        informationCollector: () => <DiagnosticsNode>[
          DiagnosticsProperty<ChangesNotifierHelperMixin>(
            '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<ChangeListener?> newListeners =
          List<ChangeListener?>.filled(newLength, null);

      int newIndex = 0;
      for (int i = 0; i < _count; i++) {
        final 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;
  }
}