Skip to content
Core

Transactions and undo ​

Move two connected cards as one operation using the public diagram controller.

Loading interactive example…Open in new tab ↗
Opening the live canvas…

Try it ​

  1. Click Preview 40, then Preview 80. Both offsets are measured from the original positions; the second preview does not accumulate the first. Each preview performs move on an interaction lease.
  2. Commit, then Undo and Redo. Both cards restore together, and their connector follows the resolved ports.
  3. Undo again, preview a move and Cancel. The original positions and existing redo branch remain available.
  4. Move pair 40 performs the move verb once, without previews.

The controller owns the edit lease and cancels it on disposal, including example reset. The example retains only the lease token, not a second document snapshot. An expired lease rejects further previews and commits. Buttons report denied verbs through the shared example error surface.

Source ​

dart
Widget buildExample() {
  final edits = Expando<DiagramInteractionLease>();
  return ExampleCanvas(
    title: 'One edit, one undo',
    instructions: 'Preview the pair at 40 or 80 pixels, then commit or cancel. Each preview starts from the original positions. Undo and redo restore both cards together.',
    createController: createHistoryExample,
    actionGroups: {
      'Move or preview': ['Preview 40', 'Preview 80', 'Move pair 40'],
    },
    actions: {
      for (final distance in [40.0, 80.0])
        'Preview ${distance.toInt()}': (controller) {
          var edit = edits[controller];
          if (edit == null || !edit.isActive) {
            edit = controller.beginInteraction('Move pair');
            edits[controller] = edit;
          }
          edit.perform(pair, 'move', {
            'delta': DiagramPoint(distance, 0),
          }).requireAllowed();
        },
      'Commit': (controller) {
        final edit = edits[controller];
        if (edit == null) throw StateError('Preview a move first.');
        edit.commit().requireAllowed();
      },
      'Cancel': (controller) => edits[controller]?.cancel(),
      'Move pair 40': (controller) => controller.edits.batch('Move pair', () {
        controller.edits.move(const DiagramPoint(40, 0), ids: pair);
      }).requireAllowed(),
    },
  );
}

See Undo and redo for admission, history ownership, selection restoration and revision behavior.