Extension reference
The foundation includes basic shapes and port markers, Plain Text, the default connector type with straight routes, base arrowheads, interaction tools and the core verbs through diagramCoreExtensions, which every DiagramExtensionRegistry installs first. Optional extensions are installed explicitly; vyuh_diagram_flutter does not depend on them.
- Table extension
- Rich-text extension
- Connectors: routes and arrowheads
- Arrowheads
- Graph layout
- Shapes library
An extension is an id, the extensions it depends on, its options and typed contribution lists, each keyed by type: shapes (by shape type), textTypes (by text type), connectors (by connector type), routes (by route name), arrowheads (by kind), portShapes (by port shape), contents (by content type), verbs, tools and graphLayouts. Shapes compose these parts by type reference. A Flutter extension adds presentation and interaction only when needed. Ordinary shape libraries render through the shared primitives.
Install concrete types such as RichTextExtension(), TableFlutterExtension() and EditorExtension() in the configuration's one extensions list; each brings its configuration as constructor arguments and its runtime options as a typed value (see extensions and options). A custom portable extension constructs DiagramExtension(...) inline or subclasses it when it carries members of its own; a custom Flutter extension constructs or subclasses DiagramFlutterExtension.
Libraries
Extension authoring types live in a second library, so application code sees only the application surface. An extension file imports both:
dart
import 'package:vyuh_diagram_flutter/extension_api.dart';
import 'package:vyuh_diagram_flutter/vyuh_diagram_flutter.dart';| Library | Holds |
|---|---|
vyuh_diagram_flutter.dart (or vyuh_diagram_core.dart in pure Dart) | The application surface: DiagramExtension, DiagramController and its verbs, typed property fields, gestures and keys, diagnostics and repairs, the document model, DiagramTool, options such as GridOptions, and in Flutter DiagramCanvas and DiagramTheme. |
extension_api.dart | Everything an extension contributes or reads: ShapeDefinition and its parts, ShapeLibrary, ShapeRegistry, value fields, DiagramTextDefinition, DiagramToolDefinition, DiagramShortcut, DiagramVerb and DiagramVerbTarget, DiagramConnectorDefinition, route providers, arrowhead and port-shape definitions, DiagramContentDefinition, DiagramProperty and its schema, DiagramMoveAdjuster, GraphLayoutAlgorithm, DiagramExtensionRegistry, DiagramOutput and the resolved scene. The Flutter extension_api.dart adds DiagramFlutterExtension, DiagramCanvasExtensionHost, DiagramCanvasBehavior, ShapeWidgetBuilder, DiagramKeyBinding, DiagramPropertyEditor, EffectDefinition and FlutterDiagramTextLayoutEngine, and re-exports package:vyuh_diagram_core/extension_api.dart. |
adapter.dart | Engine internals for tooling: transactions and steps, editor state, the resolver, render plan, painters, RenderingExtension and TextElementDefinition. |
testing.dart | Test helpers: editingExtension(), canvas surface keys and testRegistry(...). |
A package that extends another extension imports that package's extension_api.dart for its model: package:vyuh_diagram_extension_table/extension_api.dart (table cells, rows, codec, layout) and package:vyuh_diagram_extension_rich_text/extension_api.dart (rich paragraph and run attributes, richParagraph, RichTextFormatting). Inspector building blocks are in package:vyuh_diagram_extension_editor/controls.dart.
Related foundation APIs
Shapes, Plain Text, canvas, extensions and options, persistence, output, widgets, navigation and motion, diagnostics and history.
Define an extension
dart
import 'package:vyuh_diagram_flutter/extension_api.dart';
import 'package:vyuh_diagram_flutter/vyuh_diagram_flutter.dart';
final card = ShapeDefinition(
type: 'app.card',
body: const ShapeText('body', text: 'A custom card'),
behavior: const ShapeBehavior(rotatable: false),
);
final controller = DiagramController(
configuration: DiagramConfiguration(extensions: [cardsExtension()]),
);
DiagramExtension cardsExtension() => DiagramExtension(
id: 'app.cards',
shapes: [card],
tools: DiagramToolDefinition.forShapes([card], group: 'app.cards'),
);Use the Flutter imports in a Flutter host; model-only packages import package:vyuh_diagram_core/vyuh_diagram_core.dart and package:vyuh_diagram_core/extension_api.dart. Stable, unique type IDs identify saved instances. Install all required definitions before opening a document. An id is namespaced lowercase segments, such as app.cards or vyuh.table. Duplicate conflicting ids and unresolved type references are rejected. DiagramToolDefinition.forShapes(shapes, group: ...) returns one palette creation tool per shape; add them to tools beside the shapes. A contributed tool, property, option or group carries its own DiagramIcon: a name plus an optional portable svg glyph. The editor shows its own icons for core tools (defaultDiagramToolIcons) and otherwise draws the declared glyph (diagramToolIcon); both are in package:vyuh_diagram_extension_editor/controls.dart. An extension's icons travel with it, as the table tool's icon and rich text's RichTextIcons do.
dart
const DiagramExtension({
required String id,
List<DiagramExtension> dependencies = const [],
List<ShapeDefinition> shapes = const [],
List<DiagramTextDefinition> textTypes = const [],
List<DiagramConnectorDefinition> connectors = const [],
List<DiagramConnectorRouteProvider> routes = const [],
List<DiagramArrowheadDefinition> arrowheads = const [],
List<DiagramPortShapeDefinition> portShapes = const [],
List<DiagramContentDefinition> contents = const [],
List<DiagramVerb> verbs = const [],
List<DiagramToolDefinition> tools = const [],
List<GraphLayoutAlgorithm> graphLayouts = const [],
List<DiagramTool>? palette,
Object? options,
DiagramTextLayoutEngine? textLayout,
DiagramOutput? output,
})| Contribution | Keyed by |
|---|---|
ShapeDefinition | Shape type; carries its own verbs |
DiagramTextDefinition | Text type (type, such as richText); each is also a type of the core standalone text element |
DiagramConnectorDefinition | Connector type ('connector' by default) |
DiagramConnectorRouteProvider | Route name |
DiagramArrowheadDefinition | ArrowheadKind |
DiagramPortShapeDefinition | PortShape |
DiagramContentDefinition | Content type; a matching layout provider and versioned codec |
DiagramToolDefinition | Tool id |
GraphLayoutAlgorithm | Layout type, such as TreeLayout |
Beside its contributions, an extension may declare:
| Field | Meaning |
|---|---|
dependencies | Extension instances installed with it, with those defaults, unless the application installs an extension with the same id. |
options | Its typed options value, keyed by Dart type with exactly one owner; see options. |
palette | The tools offered, in order. At most one installed extension declares a palette (the editor's tools:); without one, every installed tool the grammar supports is offered. |
textLayout, output | Text shaping and SVG/PNG output for hosts without a mounted canvas; at most one installed extension supplies each. |
Override propertiesFor(scope) to contribute property-panel entries, such as controls for the extension's own options.
Assemble the grammar
DiagramExtensionRegistry(extensions: [...]) is the one place installed extensions are assembled into a grammar; a diagram builds it from the configuration's extensions and exposes it as controller.diagram.registry.
- The application's extensions are installed.
- Each one's
dependenciesare installed, with their defaults, unless an extension with the sameidalready is. Dependencies must be acyclic. diagramCoreExtensionsfill in as defaults. An installed extension with a core id, such asGridExtension(vyuh.core.grid), replaces that core extension.
A contribution with the same key as a core one replaces it, so an extension can supply its own rectangle, text type, connector type or select tool. Two non-core extensions contributing the same key conflict, as do two distinct extensions with one id and two extensions declaring the same options type; assembly rejects each with an ArgumentError naming both.
The registry exposes the assembled maps (shapes, textElements (the core's standalone text element definition for each installed text type), connectors, arrowheads, portShapes, tools, contents, contentLayouts, graphLayouts, routes, verbs), the codecs (DiagramModelCodecs) that persist installed content and text attributes, and the validated ShapeRegistry the resolver and controller read through the shapeRegistry getter:
dart
import 'package:vyuh_diagram_extension_table/vyuh_diagram_extension_table.dart';
import 'package:vyuh_diagram_flutter/extension_api.dart';
final shapes = DiagramExtensionRegistry(
extensions: [cardsExtension(), TableExtension()],
).shapeRegistry;Text elements are keyed by type: the core extensions register text (plain) and RichTextExtension() registers richText. These keys name standalone text elements. Shape slots, table cells and connector labels choose their text with a textType definition, which is plain unless rich text is given.
Add Flutter behavior when needed
DiagramFlutterExtension extends DiagramExtension: it takes every portable argument above plus its Flutter contributions. Construct one, or subclass it and pass the same arguments to super(...):
dart
const DiagramFlutterExtension({
required String id,
// ...every DiagramExtension argument...
bool editable = false,
DiagramCanvasContainerBuilder? container,
List<DiagramKeyBinding> shortcuts = const [],
Map<String, ShapeWidgetBuilder> widgets = const {},
List<EffectDefinition> effects = const [],
List<DiagramPropertyEditor> propertyEditors = const [],
DiagramPasteHandler? paste,
})Install it in the configuration's extensions like any other; the mounted canvas uses the installed Flutter extensions. A Flutter extension that needs a portable one declares it in dependencies, so applications list one class per feature: TableFlutterExtension installs TableExtension.
| Member | Contributes |
|---|---|
editable | Whether it makes the canvas edit. The editor is editable; without an editable extension the canvas navigates and selects only. |
container | A widget around the canvas; the first installed container is innermost. |
shortcuts, widgets, effects | Key bindings, shape widget builders and effects. |
propertyEditors | Inspector controls for its custom properties. |
paste | Handles external text pasted on the canvas, such as Mermaid source: (controller, text) async => handled. |
createBehavior(host) | Canvas input behavior while the canvas edits. |
createMoveAdjuster(controller) | A selection-move adjuster, such as snapping. |
build(context, host) | A viewport overlay, such as a minimap. |
A Flutter extension that adjusts selection moves overrides createMoveAdjuster(controller) to return a portable DiagramMoveAdjuster. The canvas applies installed adjusters in installation order and shows the guides they report as the host's moveGuides; the snap-guides extension contributes its alignment snapping this way.
Inspector properties come from the property grammar: an installed route provider, content provider or text formatting contributes properties by implementing DiagramPropertySource, an extension contributes its own through propertiesFor(scope), and a Flutter extension renders its custom properties through propertyEditors.
Ordinary composed shapes use shared rendering and need no Flutter extension. Use UI contributions for custom interaction and controls; perform controller verbs for edits so selection, validation and undo remain consistent. See verbs and keyboard bindings, widgets and tables for concrete integrations.
Portable extensions own verbs and tools. A shape's own verbs belong on ShapeDefinition(verbs: [...]); element and diagram verbs belong on the extension. Flutter key bindings, DiagramKeyBinding, name an installed verb; they do not define a separate mutation path, and assembly rejects a binding whose verb is not installed. An application can install its own extension alongside packaged extensions. EditorExtension() is the editable Flutter extension: it makes the canvas edit with every installed tool the grammar supports, or EditorExtension(tools: [...]) to restrict and order them, and composes a palette, inspector and toolbar around the canvas. Its container callback receives (context, controller, canvas) for a custom layout. Without an editable extension the canvas renders and navigates but does not edit through user input. See editor composition.
To change a core part, contribute a part with the same key: a text definition whose type is text replaces plain text, and a DiagramConnectorDefinition(...) without a type replaces the default connector. A Flutter extension that draws chrome keeps its colors in its own options and picks a light or dark default by DiagramTheme.brightness; see theming.
Any extension can be built without subclassing. To install a ShapeLibrary, pass its shapes and DiagramToolDefinition.forShapes(library.shapes, group: library.id) to a DiagramExtension. DiagramFlutterExtension(...) does the same for the Flutter half.
A Flutter extension that draws an overlay or joins the canvas input pipeline subclasses DiagramFlutterExtension and overrides build(context, host) or createBehavior(host). The DiagramCanvasExtensionHost exposes the controller, a camera (zoomBy, setZoom, setCenter, fitBounds, fitWidth, worldToScreen, screenToWorld, state), the selectedIds, the hoverScreenPoint, moveGuides and the shared demand-driven frameClock. Read these inside an Observer so the overlay rebuilds when they change:
dart
import 'package:flutter/widgets.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:vyuh_diagram_flutter/extension_api.dart';
final class ZoomBadge extends DiagramFlutterExtension {
const ZoomBadge() : super(id: 'app.zoom_badge');
@override
Widget build(BuildContext context, DiagramCanvasExtensionHost host) =>
Observer(
builder: (_) => Text('${(host.controller.camera.zoom * 100).round()}%'),
);
}Route edits through host.controller, for example canPerform and perform; canonical document and resolved geometry remain owned by the diagram. build renders on both edit and view surfaces; createBehavior runs only while the canvas edits.
Persist custom content
Standard shape geometry, properties and text use the shared document codec. Structured content contributes DiagramContentDefinition(layout: ..., codec: ...); the provider and codec must report the same content type. A text definition persists the paragraph and run attributes it admits through its codec (DiagramTextCodec). The registry collects both into codecs, and controller.save(DiagramJsonCodec.new), controller.edits.load(..., format: DiagramJsonCodec.new, ...) and DiagramController.open(..., format: DiagramJsonCodec.new, configuration: DiagramConfiguration(extensions: [...])) build the codec over them, so hosts never wire extension codecs by hand. The JSON format page shows where values, attributes and content appear. See persistence and output for host-facing contracts.