Persistence and imports
DiagramJsonCodec serializes the immutable document. Its current format is version 14, with decoding for versions 6–14. DiagramJsonCodec.currentVersion and minimumSupportedVersion expose those boundaries. Use the same shape registry when restoring custom shape types.
dart
final json = const DiagramJsonCodec().encode(session.state.document);Camera state, grid settings, host callbacks and runtime path animations are session configuration or presentation, not saved document content.
Import into an existing session
Capture the revision before waiting for a file or server response:
dart
final expectedRevision = session.state.document.revision;
final json = await loadFile(); // Supplied by your application.
final decision = session.importJson(
json,
expectedRevision: expectedRevision,
);
if (!decision.allowed) {
showMessage(decision.reason ?? 'Import was not allowed.');
}For an already decoded document, call session.replaceDocument(document, expectedRevision: ...).
Replacement uses one canonical transaction. It validates the imported content against the session registry, consults hooks.beforeChange, clears the current selection and emits one completed change with import origin. Undo restores the previous document and selection; redo restores the imported document. Current camera and configuration remain in place. Imported revision numbers are normalized to the store's monotonic publication sequence.
A different document ID represents a different identity scope. Even if both documents contain a shape named shape-1, the change delta reports removal from the old document and insertion into the new one.
Rejection and recovery
DiagramRevisionConflictmeans content changed while the import was being prepared. Ask the host to resolve the conflict or reload; do not blindly retry with the newest revision.- A denied permission returns a
DiagramPolicyDecisionwithout replacing the document or adding history. - Decode or registry-validation errors leave the published document unchanged.
- An owned pointer gesture must be committed or cancelled before replacement. Imports do not silently take over a drag.
- Replacing during text editing closes the old editor. A stale text client cannot mutate reused IDs in the imported document.
Initial loading into a new DrawingSession validates its snapshot but does not emit an import operation or replay creation hooks. Authorizing access to the initial snapshot belongs to the host.
Current boundaries
This API replaces a complete snapshot. It does not merge two documents, remap pasted IDs or synchronize concurrent agents. Clipboard commands retain their separate merge semantics while using the same transaction authority.
Core JSON migrations and explicit host document migration chains are supported, as described below. PNG output is available through Flutter; SVG and PDF output are not implemented. Runtime animation bindings are not imported or reset by snapshot replacement; hosts that bind presentation to document identity should update those bindings from the import change event.
Core format migrations
The decoder upgrades a private parsed payload through consecutive version steps before constructing immutable model values. Current-format decoding does not reinterpret registry shape IDs.
| Step | Meaning |
|---|---|
| 6 → 7 → 8 | Preserve existing fields; compatible optional fields retain their established defaults. |
| 8 → 9 | Convert legacy connector text into labels with stable, collision-free attachment IDs. |
| 9 → 10 | Add the historical badge presentation with padding that fits small labels. |
| 10 → 11 | Normalize legacy diamond shapes to polygons, defaulting to four sides. |
Version 11 preserves a custom registry type named diamond verbatim. Older files retain their historical polygon interpretation; they cannot distinguish a former built-in alias from an intended custom type using that same name. Versions 11 and 12 preserve that distinction.
Malformed JSON fields, unsupported versions and unknown closed-grammar enum values throw FormatException. Registry-specific semantic validation remains at the canonical session/store admission boundary. Unknown custom shape IDs are preserved by the codec and must exist in the restoring session's registry.
Standalone text migration
Version 12 writes TextElement as kind: text, with frame, rotation, parent, stacking order, required story, sizing mode, and content scroll offset. Run colors and other rich-text formatting are preserved. Shape-only fields are absent.
Versions 6–11 migrate legacy kind: shape, type: text records into this distinct element. IDs, placement, story blocks, runs, marks, colors, sizing, and scroll remain intact. Previously suppressed decoration is removed; a rectangle fill is never converted into text color.
Migration rejects legacy text with nonempty ports, owned children, bound connector endpoints, or a missing story. It does not silently orphan relationships or invent story identities. Resolve those incompatible records before importing. The current format also rejects the old shape representation and shape-specific fields on kind: text records.
Object lock persistence
Version 13 adds a required boolean isLocked to every shape, text element, and connector. Versions 6–12 migrate with isLocked: false. Copies preserve the flag unless explicitly changed; malformed flags in current-format JSON are rejected. The store enforces locks for new changes and inherited ownership. The inspector exposes a lock toggle and hides editing fields while locked. Undo/redo replays admitted history and still consults host admission.
Host schema migrations
DiagramDocumentMigrations upgrades a decoded document through a complete, immutable chain of host-defined steps. Host schema versions are independent of DiagramJsonCodec.currentVersion: store your host version alongside the diagram in your application's persistence envelope.
| API | Arguments | Contract |
|---|---|---|
DiagramDocumentMigration(...) | fromVersion: int, upgrade: DiagramDocument Function(DiagramDocument) | Upgrades one host version to fromVersion + 1. |
DiagramDocumentMigrations(...) | minimumSupportedVersion: int, currentVersion: int, steps: Iterable<DiagramDocumentMigration> | Copies and validates the complete chain. Rejects gaps, duplicates and steps outside the supported range. Versions must satisfy 0 <= minimum <= current. |
migrate(document, fromVersion: ...) | Immutable decoded document and its persisted host version | Runs steps in version order. Unsupported versions throw FormatException; callback failures propagate and stop subsequent steps. Current-version input is returned unchanged. |
dart
final migrations = DiagramDocumentMigrations(
minimumSupportedVersion: 1,
currentVersion: 3,
steps: [
DiagramDocumentMigration(fromVersion: 1, upgrade: upgradeV1ToV2),
DiagramDocumentMigration(fromVersion: 2, upgrade: upgradeV2ToV3),
],
);
final expectedRevision = session.state.document.revision;
final saved = await loadHostEnvelope(); // Host-owned storage and envelope.
final decoded = const DiagramJsonCodec().decode(saved.diagramJson);
final migrated = migrations.migrate(decoded, fromVersion: saved.schemaVersion);
final decision = session.replaceDocument(
migrated,
expectedRevision: expectedRevision,
);Migration creates candidate immutable values; it does not publish intermediate versions or add history. The final replaceDocument still checks the expected revision, registry, geometry and host admission hook. Persist the current host version only with a successfully accepted and saved document.
Upgrade callbacks must preserve or deliberately remap identities, ownership, ports, stories and connector references as their schema requires. They must be deterministic and free of external side effects: the adapter cannot roll back host I/O. This API handles whole-document host upgrades after core decoding; it does not provide raw malformed-JSON repair, live registry replacement, concurrent merging or automatic inference of shape-schema changes.
Label borders
Version 14 adds badge stroke, strokeWidth, and strokeStyle. Versions 6–13 migrate with a zero-width border, retaining existing appearance. Current-format badge fields are required and validated. Plain labels cannot carry decoration. Older readers reject version 14 rather than silently discarding label borders.