7. Add a custom review node
Commercial extension tutorial · Built on the free Core + Flutter foundations. Start with the free Hello World example.
Now give our flowchart a reusable review node with left and right ports. Shape definitions are extension authoring types, so first add their library beside the existing imports:
dart
import 'package:vyuh_diagram_flutter/extension_api.dart';Then add this top-level definition below the app classes:
dart
ShapeDefinition reviewNode() => ShapeDefinition(
type: 'tutorial.review',
outline: const ShapeOutline.rect(),
body: const ShapeText('body', horizontalPadding: 16, verticalPadding: 16),
minimumSize: const DiagramSize(140, 80),
size: const DiagramSize(220, 120),
style: const ShapeStyle(cornerRadius: 12),
behavior: const ShapeBehavior(
rotatable: false,
textSizing: ShapeTextSizing.growHeight,
),
connection: ShapeConnectionDefinition.fixedPorts([
PortDefinition.onSide(id: 'input', side: PortSide.left),
PortDefinition.onSide(id: 'output', side: PortSide.right),
]),
);Install it through one extension on the controller. Replace the controller initializer:
dart
late final controller = DiagramController(
configuration: DiagramConfiguration(
extensions: [
EditorExtension(inspector: InspectorPlacement.none, toolbar: false),
reviewExtension(),
],
),
);
DiagramFlutterExtension reviewExtension() =>
DiagramFlutterExtension(id: 'tutorial.review', shapes: [reviewNode()]);In both the initial process creation and addProcess, replace Shapes.rectangle with 'tutorial.review'. Hot restart because the controller is created once. Existing saved rectangles do not automatically become the new type.
Before restarting, also replace the two connection calls in initState. A ports-only review node requires an explicit port ID on its endpoint:
dart
controller.edits.connect(startId, processId, toPort: 'input');
controller.edits.connect(processId, decisionId, fromPort: 'output');The other endpoints still bind to the standard shapes' outlines. Omitting a review port is invalid; the SDK does not silently choose an outline attachment for a ports-only shape.
The definition is the node's anatomy: a rounded rectangle outline (so fill, stroke and corner radius are editable), one padded body text part, two fixed ports and its behavior. The instance owns its ID, position and text. Shared code handles text editing, hit testing, movement, port placement, routing, saving and history. You do not implement a second painter/drag pipeline.
Behavior, locks and host permissions
| Mechanism | Question it answers | Example |
|---|---|---|
| Definition anatomy and behavior | What edits does this type support? | Review nodes cannot rotate. |
| Instance locks | Is this object currently locked? | A finalized node cannot move. |
| Host admission | Is this candidate edit permitted now? | The current user has read-only access. |
Unlocking a node does not grant an edit its definition does not support. All three can affect the result returned by an edit. This is why handling allowed belongs in custom controls too.
Pass DiagramController(configuration: DiagramConfiguration(rules: DiagramRules(beforeChange: ...))) when you need host-wide admission; return EditResult.denied(reason: 'This drawing is read-only.') or const EditResult.allow(). It is synchronous and read-only, and it runs for every edit from any controller of the diagram. Server authorization remains your backend's responsibility. Check fixed domain invariants in the same rule against the proposed document: beforeChange: (proposal) => validate(proposal.after); see hooks.
Checkpoint: add a review node, edit its text, connect through its ports and save/load it. It has no rotation interaction. For richer compositions and native controls, continue later with Build a library.