Flutter docs
Default skin

DiagramEditor Composition

Draws and edits a diagram of nodes joined by arrows. The caller owns the nodes and edges; every change is a request the caller applies, or does not.

Usage

Drag a kind in, or tap it. Drag or click between handles to connect; from the keyboard, C then C. F2 renames.

  • Draft
    • → Review
  • Review
    • → Approved: Yes
    • → Rework: No
  • Approved
  • Rework

Interaction contract

The editor runs on the React Flow runtime (@xyflow/react) and keeps its interaction model rather than re-implementing one. Nodes and edges are focusable; Enter or Space selects the focused one, arrow keys move selected nodes one grid cell, and Delete or Backspace asks to remove the selection. With a pointer, drag a node to move it, drag from one handle to another to connect — or click one handle and then another, which works on touch — and drag the background to pan. Scroll or a two-finger swipe pans (Shift + scroll pans sideways); pinch, Ctrl/⌘ + scroll and the − / + / fit controls zoom — a wheel that zoomed fought the page and every trackpad habit. The read-only Diagram does the same.

Adding a node from the palette

palette lists the kinds a person can add, each drawn in its node shape with its description as a tooltip. Drag one onto the canvas and onAddNode({ kind, position }) asks for it centred where it was dropped, in whole cells. A drag is not the only way: a tap, a click or Enter asks for it in the middle of what is visible, so touch screens and keyboards can add nodes too (WCAG 2.5.7). Like every change it is a request — the node appears when you add it to nodes.

Renaming in place

Set editingId to a node's id and it shows an input where its label was — set it to the id of a node you just added, so a new node is named where it sits. Enter or leaving the input calls onRenameSubmit(id, label); Escape, or an empty or unchanged name, calls onRenameCancel. F2 still reports onRename({ kind, id }), so a caller can start the same inline rename from the keyboard.

Nothing is applied by the component. A move is reported in whole cells when the drag ends or the key is pressed, and snaps back unless the caller updates nodes; a connection, a removal (a node brings the edges touching it) and a rename are requests too. The component does not know what a valid diagram is.

React Flow has no keyboard way to connect, so this component adds one: C on a focused node begins a connection and C on another node commits it, announced through a status message; C on the starting node or Escape cancels. F2 asks to rename the focused node or edge — the caller renders the input.

A selected node says what it is through renderInspector: the editor hands it the selection described in a person's terms — the node with where it leads and what leads to it, or an edge with both ends — and draws the consumer's panel beside the canvas, or below it on a narrow screen. Put the actions there as real buttons (rename, connect, remove); the shortcuts stay as accelerators, and on a phone the panel is the only way to act. The diagram opens fitted, but never below a readable zoom; labels wrap to two lines with the full text as a tooltip; legendLabels names the node shapes in use.

Do

Keep nodes and edges in state and apply each request, rejecting the ones your rules forbid.

Don't

Treat onRemove as already done, or put interactive controls inside a node's content.

Props

PropTypeDefaultNotes
labelstring—Accessible name for the editor viewport.
nodesDiagramNode[]—id, label, position in grid cells, optional shape, tone and static content.
edgesDiagramEdge[]—id, from, to, optional label and tone.
selectedIdstring | null—The selected node.
onSelectionChange(id) => void—Called when a different node becomes selected.
onMoveNode(id, position) => void—A move request in whole cells, from a drag or an arrow key.
onConnect(from, to) => void—A connection request. Never called with from equal to to.
onRemove({ nodeIds, edgeIds }) => void—A removal request; a node brings the edges touching it.
onRename({ kind, id }) => void—A rename request for a node or an edge.
connectingStatus(nodeLabel) => string—Localised announcement while connecting from the keyboard.
hintstring—Optional visible guidance above the viewport.
outlineLabelstringDiagram outlineAccessible name for the outline list.
ariaLabelsPartial<AriaLabelConfig>—Localised names and keyboard descriptions for the runtime.
renderInspector(selection: DiagramSelection | null) => ReactNode—The panel that says what is selected and offers its actions as buttons. null when nothing is selected.
inspectorLabelstringSelectionAccessible name for the inspector panel.
legendLabelsPartial<Record<DiagramShape, string>>—Names for the node shapes; given, a legend lists the shapes in use.
paletteDiagramPaletteItem[]—{ kind, label, shape, tone?, description? } — the kinds a person can drag or tap in.
onAddNode({ kind, position }) => void—An add request, centred on the drop point or the visible middle, in whole cells.
paletteLabelstringAdd a nodeAccessible name for the palette.
paletteHintstring—One line shown in the palette.
editingIdstring | null—The node renamed in place.
onRenameSubmit(id, label) => void—The new name, trimmed, on Enter or leaving the input.
onRenameCancel() => void—Escape, or an empty or unchanged name.
renameLabel(label) => string`Rename ${label}`Accessible name for the rename input.

Skeleton

DiagramEditor.Skeleton is the same loading state as Diagram.Skeleton.

Keyboard

KeyResult
TabMoves between nodes, edges and the controls
Enter / SpaceSelects the focused node or edge
Arrow keysRequest moving the selected nodes one grid cell
Delete / BackspaceRequest removing the selection, with the edges touching removed nodes
CBegin connecting from the focused node; while connecting, connect to it
EscapeCancel connecting; clear the selection
F2Request renaming the focused node or edge

Tokens

--cb-space-5 (one grid cell), --cb-canvas-block-size, --cb-surface, --cb-border*, --cb-tone-*, --cb-fg*, --cb-focus-*, --cb-radius-* and --cb-text-*.