Skip to content

Transactions, history and previews

Implementation contracts for custom engine integrations. Ordinary applications use the session commands and undo/redo API.

DiagramTransaction

Use a transaction when several related changes must publish and undo together.

dart
DiagramTransaction({
  required List<DiagramStep> steps,
  DrawingSelection? selectionAfter,
  String label = 'Edit diagram',
})
PropertyTypeDefaultContract
stepsList<DiagramStep>RequiredOrdered, unmodifiable copy. Each step receives the preceding step's document.
selectionAfterDrawingSelection?nullOptional resulting selection. Otherwise retain the current selection, then map it against the resolved result.
labelString'Edit diagram'Human-readable operation label retained for history and change events.
dart
final decision = session.dispatch(DiagramTransaction(
  label: 'Move pair',
  steps: [
    MoveShapeStep(
      elementId: firstId,
      delta: const DiagramPoint(40, 0),
    ),
    MoveShapeStep(
      elementId: secondId,
      delta: const DiagramPoint(40, 0),
    ),
  ],
));
decision.requireAllowed();

Steps are evaluated and the complete candidate is validated and resolved before publication. If a step throws, validation fails, or admission denies the candidate, the transaction does not partially publish. Direct transaction.apply(state) only computes a candidate; use session.dispatch to perform an edit with validation, history and notifications.

DiagramStep

dart
abstract interface class DiagramStep {
  DiagramDocument apply(DiagramDocument document);
}

Custom steps return a new immutable document. They must not mutate the input or call back into the store to publish another edit. The store applies its normal validation, lock checks and host admission to their result.

Built-in stepConstructor argumentsEffect
InsertElementStepPositional DiagramElement elementInsert one element; rejects an existing ID.
InsertElementsStepPositional List<DiagramElement> elementsInsert a batch; rejects duplicate or existing IDs.
RemoveElementStepPositional String elementIdRemove one existing element. Prefer the removal command when connected or owned elements need cleanup.
RemoveElementsStepPositional Set<String> elementIdsRemove the canonical removal closure, including connectors that would retain dangling bindings.
ReplaceElementStepRequired named DiagramElement before, DiagramElement afterReplace an unchanged expected element; requires the same ID.
MoveShapeStepRequired named String elementId, DiagramPoint deltaTranslate an existing framed element.
ResizeShapeStepRequired named String elementId, DiagramRect frameReplace a framed element's bounds; width and height must be positive.
RotateShapeStepRequired named String elementId, double rotationSet a framed element's rotation in radians.
ReplaceTextStoryStepRequired named String elementId, TextStory before, TextStory afterReplace the expected story within its text owner.

Stale before values throw StateError rather than overwriting newer edits. For complete document replacement and its revision check, use persistence and import.

Advanced: history groups

These methods belong to an explicitly owned headless DiagramStore, not the session surface. DrawingSession.dispatch does not accept a group token. Use one transaction for a synchronous batch; use a group when several successive publications belong to one operation.

MethodSignatureContract
beginHistoryGroupDiagramHistoryGroup beginHistoryGroup()Close the previous group and obtain ownership of a new group. Requires no active interaction.
dispatchDiagramPolicyDecision dispatch(DiagramTransaction transaction, {DiagramHistoryGroup? historyGroup})With a current token, publish a provisional grouped edit; without one, close the group and dispatch an independent edit.
ownsHistoryGroupbool ownsHistoryGroup(DiagramHistoryGroup token)Check whether the token still owns the group.
endHistoryGroupDiagramPolicyDecision endHistoryGroup(DiagramHistoryGroup token)Admit the final change and close it. A stale token is an allowed no-op.
cancelHistoryGroupvoid cancelHistoryGroup(DiagramHistoryGroup token, {String label = 'Cancel grouped edit'})Restore the baseline and prior redo branch. A stale token is a no-op. The current implementation does not emit a cancellation event or use its label.

DiagramHistoryGroup is an opaque token obtained from the store; it has no public constructor. Its read-only int operationId identifies this operation within the store lifetime. The first content-changing transaction supplies the retained group label.

An unrelated dispatch, undo/redo, new history group or pointer interaction ends ownership. Dispatching with an expired token throws StateError. Group ownership is not an exclusive lock across asynchronous host work: check ownership before continuing and submit a fresh intent when it has been superseded.

Advanced: interaction previews

Try the transactions and undo example for a session-owned multi-element preview, commit, cancellation and redo recovery. Each lease.preview(transaction) evaluates from the original edit baseline. An expired lease returns a denied decision for preview and commit; its cancel is a no-op. Check decisions with requireAllowed() or handle the reason in your UI.

Custom tools can use the same engine interaction boundary. Built-in canvas tools already manage this lifecycle. Retain the returned DiagramInteractionLease when callbacks can outlive their gesture; it prevents a delayed callback from modifying a newer interaction.

MemberSignatureContract
isInteractingbool get isInteractingWhether an interaction owns the preview baseline.
interactionStartDiagramEditorState? get interactionStartImmutable baseline, or null when idle.
beginInteractionDiagramInteractionLease beginInteraction(String label)Close any history group, capture the baseline and return its ownership lease. Throws StateError if another interaction is active.
previewInteractionDiagramPolicyDecision previewInteraction(DiagramTransaction transaction)Evaluate from the original baseline, not the previous preview. Requires an active interaction.
commitInteractionDiagramPolicyDecision commitInteraction()Recheck admission and record one nonempty history entry. An idle call is an allowed no-op.
cancelInteractionvoid cancelInteraction()Restore the baseline without a history entry. An idle call is a no-op.

DiagramInteractionLease

The lease has no public constructor. Obtain it from store.beginInteraction. Prefer its scoped methods over unscoped store.previewInteraction and store.commitInteraction when implementing a custom tool or asynchronous host control.

MemberSignatureContract
isActivebool get isActiveWhether this lease still owns the live store interaction.
previewDiagramPolicyDecision preview(DiagramTransaction transaction)Preview from this interaction's baseline. Returns denial when the lease is inactive.
commitDiagramPolicyDecision commit()Commit this interaction. Returns denial when inactive, even if another interaction now exists.
cancelvoid cancel()Cancel only this interaction; an inactive lease is a no-op.

A lease becomes inactive after completion, cancellation or store disposal. Unlike the unscoped store methods, stale callbacks cannot affect a successor interaction. For scalar shape controls, the value-edit wrapper builds the transactions and owns this lease for you.

For cumulative movement, supply the total delta from gesture start on each preview. Applying an incremental pointer delta here would repeatedly restart from the baseline. Denied previews restore the baseline while retaining ownership; a later permitted preview can recover. Denied commits restore the baseline and end the interaction.