Skip to content

API layers and ownership

Start a Flutter integration with DrawingSession, DrawingConfiguration and DrawingCanvas from vyuh_diagram_kit. The other public packages expose lower-level contracts for headless agents, custom tools and custom rendering. An exported type is not necessarily an object that every host must create.

The application-facing model

ObjectWhat your application uses it for
DrawingSessionCreate, connect, select, transform, lock, delete, undo and redo.
session.documentRead immutable content and save it with encode().
DrawingCanvasMount the interactive surface.
DrawingConfigurationConfigure fonts, grids, canvas behavior, extensions and widget builders.
ShapeRegistry / ShapeDefinitionRegister durable geometry, capabilities, text slots, values and portable content.
ShapeWidgetRegistrySupply Flutter builders for the widget keys declared by those definitions.
Shapes / DiagramToolsConvenient catalogs of standard registrations; custom keys remain open-ended.

Applications do not construct command-group objects or manage a store to embed a drawing. Session listeners take no state argument: read document, selection, activeTool or configuration after notification. snapshot pairs the immutable document with its resolved scene and excludes editor bookkeeping.

DiagramStore, internal editor state and DiagramJsonCodec are not convenience exports of the kit. The sections below describe explicit lower-level integrations, which import their owning packages directly.

Advanced integration layers

LayerMain objectsUse it forMutation authority
Flutter hostDrawingSession, DrawingConfiguration, DrawingCanvasMounting an editor, commands, configuration and subscriptionsThe session's DiagramStore
Canonical engineDiagramStore, commands, DiagramTransaction, DiagramInteractionLeaseHeadless agents, custom commands and continuous previewsThe same store; admission precedes publication
Schema and grammarDiagramDocument, element/story values, ShapeRegistry, definitions and capabilitiesDescribing content and the operations each type supportsImmutable inputs; submit changes through commands
Native widget presentationShapeWidgetRegistry, ShapeWidgetBuilderSupply Flutter controls for registered widget slots through DrawingConfiguration.widgetRegistryBuilders read canonical values; edits use the same session/store commands
Resolved geometryDiagramResolver, ResolvedScene, outlines, routes and text linesDeriving geometry for a document and its registryDerived output; never edit a resolved scene to change content
Flutter projectionDrawingCanvasSurface, DrawingCanvasController, DiagramSceneCoordinator, painters and handle projectionsBuilding a deliberate lower-level mounted integrationThe supplied store; adapters do not own another document
Output adaptersJSON codecs, SVG scene/resources, PNG/SVG export functionsPersistence and exportCapture canonical content or an admitted publication

The kit exposes grammar, transactions, tools, extensions and output types so hosts can compose them without private imports. It hides the mounted editor, controller, coordinator, painter, paint cache and DiagramEditorSettings names. Use the owning package explicitly when implementing that lower-level integration.

The kit also excludes the mounted DiagramCameraController, content-band and animation/preview painters, freehand/move previews, control-handle types, selection/text-caret projections and transform-cursor projections. These remain public from vyuh_diagram_renderer_flutter for custom mounted composition. Ordinary hosts use session navigation and registered tools; they do not construct these adapters. This is an import-boundary change: advanced consumers must add the explicit editor-package import rather than importing its private src files. There is no compatibility layer or deprecation promise implied by these names.

Inspector, extension and diagnostic imports

NameImport layerWhy it belongs there
DrawingInspectorKit host APITakes a DrawingSession; uses its configuration, history and admission.
DiagramPropertyInspectorExplicit editor importTakes a DiagramStore and optional mounted controller for advanced composition. Shape capabilities come from that store's configured registry, with no inspector-specific override. It is excluded from the kit.
DiagramInspectorTitle, DiagramInspectorSectionLabelKit host APIStateless presentation helpers for host-supplied property panels; no store or controller arguments.
DiagramPropertyStepper, DiagramVisualChoiceField<T>Kit host APIValue/callback controls for host-supplied fields; hosts submit changes through session commands.
DiagramEditorExtension, DiagramEditorExtensionControllerKit extension APICustom overlays receive read-only scene/camera projections and bounded camera commands.
DiagramFrameClock, DiagramFrameClockLeaseKit extension APIThe extension controller exposes the shared clock. Acquire demand only while animating; release the lease and listeners when the overlay stops or unmounts.
DiagramBandRenderingExplicit editor importPaint operations on DiagramScenePainter, itself an advanced mounted adapter.
DiagramFrameBudgetExplicit editor importA diagnostic 120 Hz target, not a canvas configuration or a promise of measured frame delivery.

Tests inspecting renderer surfaces import vyuh_diagram_renderer_flutter. Tests inspecting Pro controls such as the inspector use package:vyuh_diagram_kit/canvas_adapter.dart. Production host composition continues to use the session-based kit objects.

Engine services versus session commands

Application code uses DrawingSession: import vyuh_diagram_kit for Flutter or vyuh_diagram_engine for headless Dart. The engine owns the headless DrawingSession; the kit adds Flutter integration.

Store-bound command services belong to the explicit package:vyuh_diagram_engine/vyuh_diagram_engine.dart import. Flutter hosts use the session methods below, which retain the same admission, history and lifetime checks. These services and their extensions are no longer named exports of the kit.

Advanced engine serviceSession entry point
DiagramCreationCommands, DiagramConnectedCreationCommandscreateShape, createText, insertConnectedElement
DiagramObjectCommandsaddElement, removeElements, deleteSelection, setImageSource, updateValues
DiagramSelectionCommandsselectElements, selectAll, clearSelection
DiagramLockCommandssetElementsLocked
DiagramGroupingCommandsgroupSelection, ungroupSelection
DiagramClipboardCommandscopySelection, paste, duplicateSelection
DiagramArrangementCommands, DiagramArrangementOrder, DiagramArrangementResize, DiagramArrangementTransformsalign, distribute, reorder, moveElements, resizeElements, rotateElements
DiagramConnectionCommands, DiagramConnectionEditingCommandscreateConnector, reconnect
DiagramConnectorLabelCommands, DiagramConnectorLabelResizeCommandsConnector label methods on the session; advanced resize machinery stays in the engine
DiagramStoryCommandssession.text (TextEditing)

The kit hides stores, editor state, publications and store-bound command groups. An infrastructure integration using integration.borrowDrawingStore imports the engine package and the kit's adapter.dart explicitly. Store borrowing is absent from the consumer session constructors. Pure-Dart infrastructure uses the session package's adapter.dart; ordinary pure-Dart applications use vyuh_diagram_engine.dart. Transaction, policy and gesture contracts remain available for custom tools. Custom tools continue to receive DiagramToolContext and dispatch canonical transactions; they do not need to construct a store-bound command service. No service is removed from its owning package, and no compatibility alias is introduced.

DiagramToolContext.dispatch(transaction) is a void command: it returns normally when admitted and throws DiagramChangeDenied when a host policy denies the transaction. A custom tool can catch that exception and inspect decision.reason for feedback. Denial leaves the prior publication and history unchanged. The callback-scoped context still rejects use after the tool callback returns.

Resource lifetimes

The kit exposes registry definitions and resolved geometry for custom hosts, but does not expose DiagramOwnershipIndex, the rebuildable lock-topology cache. Use session.setElementsLocked for lock changes and the admitted resolved scene for effective lock state. Advanced engine integrations can import the index from vyuh_diagram_engine; applications do not need to maintain or invalidate it.

Editor-provided extension controllers belong to one camera/scene owner. Their reads and navigation commands throw StateError after store replacement or editor unmount. Refresh overlay subscriptions when buildOverlay receives a replacement controller. Ordinary rebuilds retain the owner's lifetime. The shared frame clock instead follows the editor lifetime; it survives store replacement, rejects new demand after unmount, and permits release of old leases.

ObjectWho owns it?Cleanup and failure contract
DrawingSession(...)Flutter hostUnmount its canvas before dispose(). The session disposes the store it created. Disposal is idempotent; commands after disposal reject.
integration.borrowDrawingStore(store)Host owns the facade; caller retains the storeDispose the facade after unmount, then dispose the externally owned store when finished. A borrowed facade cannot replace the owner's admission hook.
DrawingCanvasFlutter widget treeOne mounted canvas per session. Unmounting releases presentation ownership while retaining session content and history.
DiagramStoreSession or headless ownerdispose() clears subscriptions, history, callbacks and gesture ownership. Disposal during evaluation or notification rejects. Retained immutable publications remain readable.
DiagramInteractionLeaseCommand or gesture ownerPreview from its initial state; commit once or cancel. A stale lease cannot mutate a successor interaction. It is not a second store.
DiagramSceneCoordinatorAdvanced Flutter adapterdispose() removes its store listener; it does not dispose the store.
DiagramEditorAdvanced Flutter adapter, or session internallyAttach through the owning editor; release the mounted editor before discarding the controller. Do not create one alongside a session just to call content commands.
Native Pango text backendNative host that creates itDispose dependent stores first, then the backend. Supplying it to a resolver does not transfer its lifetime. See native text.
Canonical and resolved valuesCaller retaining the snapshotNo disposal; retain immutable snapshots as needed. A snapshot does not keep a disposed editor actionable.

Session lifetime in a host

Create an owning session once for the lifetime of the editor host, rather than inside build(). Removing DrawingCanvas from the widget tree releases its presentation attachment; the session retains the document and undo history for a later remount. After the canvas has unmounted, call session.dispose(). Disposing a session while its canvas is still attached throws instead of leaving a mounted editor with dead resources.

When another component owns the canonical store, borrow it explicitly:

dart
import 'package:vyuh_diagram_kit/adapter.dart' as integration;

final session = integration.borrowDrawingStore(store);
// Mount DrawingCanvas(session: session), then unmount it when finished.
final savedPublication = session.snapshot;
session.dispose();

// The externally owned store is still usable. A later host can adopt it.
final nextSession = integration.borrowDrawingStore(store);
// savedPublication remains an immutable snapshot, not a live editing handle.
nextSession.dispose();
store.dispose(); // Only the store's owner performs this final cleanup.

Disposal cancels the facade's active preview and restores its pre-edit state. It does not cancel an interaction subsequently started by another owner after that preview ended. Commands retained from the disposed session, including session.text, reject further work. Dispose is idempotent, but must happen outside synchronous document publication callbacks; schedule host teardown after the callback returns.

Borrowed facades share selection, active tools, history and interaction admission. Use this pattern to manage ownership, not to represent independent agent or user participants. See sessions and collaboration for that boundary.

Commands versus presentation

OperationCanvas required?Contract
Create, mutate, connect, lock, select, undo/redoNoUse session commands or the same store's command groups. Host authorization belongs in beforeChange, including for custom transactions.
Read document or resolved sceneNoObserve the admitted store publication; do not pair a document with geometry from another revision.
Read session.cameraStateNoReturns null while detached.
Navigate, convert coordinates, animate viewport or pathsYesSession presentation commands throw StateError while detached.
Subscribe to contentNoaddListener observes state; document-change hooks describe completed content operations. Remove subscriptions or dispose their owner.
Subscribe to presentationNoaddPresentationListener observes camera/animation changes separately from content history.

The advanced mounted controller has some detached arrangement methods that are no-ops. That behavior is not the session contract and is not a reason to route headless content operations through a controller.

Extending the API

Register shape definitions, tools, native widget builders and hooks through their public registries. Custom tools dispatch the same transactions as built-in tools. Use semantic session operations for ordinary editing. Advanced transactions use session.dispatch or session.beginEdit, without exposing the mutable store. Pure-Dart agents may own a DiagramStore from the engine package directly.

The service-map and storyboard starter compositions demonstrate contrasting registries, tools and policies using public imports. They are examples of these layers, not proof of every possible extension or production frame budget.

Flutter hosts and server agents

vyuh_diagram_engine owns the pure Dart semantic session. The Flutter kit's DrawingSession extends it with mounted configuration and presentation adapters. Content commands share one implementation and one canonical store.

HostEntry pointText layout
Flutter desktop or webDrawingSession(document: ..., shapeRegistry: ..., configuration: ...)Defaults to FlutterDiagramTextLayoutEngine. Mount DrawingCanvas for input, camera and native widgets.
Pure Dart agent or serverDrawingSession(document: ..., shapeRegistry: ..., textLayout: ...) from vyuh_diagram_engineDefaults to approximate metrics. Supply a DiagramTextLayoutEngine when actual font metrics are required.
Flutter view of an existing storeintegration.borrowDrawingStore(store)Retains the store's existing resolver and layout backend; wrapping it does not replace approximate text metrics with Flutter metrics.

Both entry points use the same store commands, admission, document publication and undo history. The session forwards those commands; it does not maintain a second document. A server imports vyuh_diagram_engine and calls session.dispose() when finished. Advanced engine integration remains available to platform adapters and domain-kit authors. Native Flutter widgets require a mounted canvas; their declared fallback content remains available to headless resolution and SVG output.

Attaching a Flutter session to an existing engine

When a Flutter canvas borrows a store, install its text backend when creating that store, before the first document is resolved:

dart
final store = DiagramStore(
  DiagramEditorState.initial(document),
  resolver: DiagramResolver(
    registry: registry,
    textLayout: FlutterDiagramTextLayoutEngine(),
  ),
);
final session = integration.borrowDrawingStore(store);

For the workflow kit, pass textLayout: FlutterDiagramTextLayoutEngine() to registry.createSession(...), then use DrawingSession.fromSession(...) for its Flutter adapter. The lower-level createStore adapter remains available for framework integration. The Flutter canvas rejects text lines or list markers without shaped geometry. It does not remeasure fixed-metric text while painting or editing. Existing headless stores must be created with the appropriate backend for their host.

dart
integration.borrowDrawingStore(
  DiagramStore store, {
  DrawingConfiguration? configuration,
})

borrowDrawingStore borrows the store. Without explicit configuration it projects the store settings into a default configuration. With configuration it applies those settings, but retains the owning store admission hook. The owner remains responsible for disposing the store.