Skip to content

6. Save and load the drawing ​

Commercial extension tutorial · Built on the free Core + Flutter foundations. Start with the free Hello World example.

Save the committed document, not widget state or resolved routes. Add this field and these methods to the State for a storage-free round trip:

dart
String? savedJson;

void saveDrawing() {
  savedJson = controller.save(DiagramJsonCodec.new);
}

void loadDrawing() {
  final json = savedJson;
  if (json == null) return;
  showDecision(controller.edits.load(
    json,
    format: DiagramJsonCodec.new,
    expectedRevision: controller.diagram.document.revision,
  ));
}

Add Save and Load buttons to buildToolbar using these callbacks. This intentionally keeps the saved text in memory; connect the same JSON boundary to your file picker or backend for durable storage.

Observe completed edits ​

For autosave, register a dedicated listener in initState:

dart
controller.diagram.changes.addListener(onDocumentChanged);

Add its callback and remove it in dispose, before disposing the controller:

dart
void onDocumentChanged() {
  final change = controller.diagram.changes.value;
  if (change == null) return;
  // final json = DiagramJsonCodec(controller.diagram.registry.codecs)
  //     .encode(change.after);
  // Enqueue json in your host's debounced, ordered persistence queue.
  // Keep file/network work outside this synchronous notification.
}
// In dispose():
// controller.diagram.changes.removeListener(onDocumentChanged);

Wire the queue when you choose storage; the callback above only illustrates the subscription. Completed changes include Undo and Redo. A canceled drag produces no completed edit to save. Order asynchronous writes so an older request cannot overwrite a newer revision, and surface storage failures in your application.

Loading after asynchronous work ​

Capture controller.diagram.document.revision before awaiting a file or network read, then pass that captured value as expectedRevision. If editing occurred while waiting, the SDK denies the stale replacement with a result whose code is DiagramDenialCode.conflict. Let the user retry or choose how to reconcile the content. Do not simply substitute the latest revision to bypass this check.

The in-memory Load above is synchronous, so capturing at invocation is sufficient. For external input, handle decode/validation errors as well as policy denial and revision conflicts.

A new controller can open JSON with DiagramController.open(json, format: DiagramJsonCodec.new, configuration: DiagramConfiguration(extensions: [...])). Install the same extensions used to create it; the codec persists the content and text each installed extension owns. Definitions, verbs, key bindings, host widgets and runtime presentation are not serialized into the document. Reinstall them when reopening.

Checkpoint: Save, move a node, Load. The saved drawing returns; Undo can reverse the accepted replacement. See persistence for codecs and the format version.