mutate<R> method

  1. @protected
R mutate<R>(
  1. R action(), {
  2. String? label,
})

Runs action, then notifies every listener that state changed.

Supports both synchronous actions and asynchronous actions (returning a Future). For asynchronous actions, listeners are notified once the returned Future completes.

Returns the result of action. Optionally pass label to override the action name for Orbit.observe middleware and debug logging — e.g. mutate(() => count++) (automatically uses 'increment'). If omitted, label is automatically inferred from the calling method name. If you also override snapshot, Orbit logs exactly which fields changed.

Implementation

@protected
R mutate<R>(R Function() action, {String? label}) {
  final tracking = Orbit.debugLogging || Orbit._hasObservers;
  // In non-release builds _notify must always run so that postEvent fires
  // and the VS Code / DevTools extension receives live state updates —
  // even when debugLogging is off and no observers are registered.
  final notify = tracking || !kReleaseMode;
  // Snapshots/label inference are gated on `notify`, not the narrower
  // `tracking`: postEvent (which powers the VS Code/DevTools extension)
  // fires whenever `notify` is true and reads `mutation.after` directly,
  // so it must always have a real snapshot to read — gating on `tracking`
  // alone left it silently empty whenever debugLogging was off and no
  // observers were registered, even with the extension attached.
  final inferredLabel = notify ? _inferLabel(label) : label;
  final before = notify ? _safeSnapshot() : null;

  try {
    final result = action();
    if (result is Future) {
      var hasError = false;
      Object? asyncError;
      StackTrace? asyncStack;

      return (result as Future)
          .catchError((Object error, StackTrace stackTrace) {
        hasError = true;
        asyncError = error;
        asyncStack = stackTrace;
        throw error;
      }).whenComplete(() {
        _dispatchNotify();
        if (notify) {
          Orbit._notify(
            this,
            OrbitMutation(
              store: this,
              action: inferredLabel,
              timestamp: DateTime.now(),
              listenerCount: _listenerCount,
              before: before,
              after: _safeSnapshot(),
              error: hasError ? asyncError : null,
              errorStackTrace: hasError ? asyncStack : null,
            ),
          );
        }
      }) as R;
    }

    _dispatchNotify();
    if (notify) {
      Orbit._notify(
        this,
        OrbitMutation(
          store: this,
          action: inferredLabel,
          timestamp: DateTime.now(),
          listenerCount: _listenerCount,
          before: before,
          after: _safeSnapshot(),
        ),
      );
    }
    return result;
  } catch (error, stackTrace) {
    _dispatchNotify();
    if (notify) {
      Orbit._notify(
        this,
        OrbitMutation(
          store: this,
          action: inferredLabel,
          timestamp: DateTime.now(),
          listenerCount: _listenerCount,
          before: before,
          after: _safeSnapshot(),
          error: error,
          errorStackTrace: stackTrace,
        ),
      );
    }
    rethrow;
  }
}