Skip to content

4. Connect reactive state to your UI ​

Commercial extension tutorial · Built on the free Core + Flutter foundations. Start with the free Hello World example.

Diagram state reaches your UI through MobX: the controller exposes read-only MobX-observable getters, and an Observer that reads them rebuilds when they change. Completed document edits also arrive as events on controller.diagram.changes. Documents remain immutable snapshots; a document reference you saved earlier never changes in place.

From an action to a visible update ​

Canvas gestures and toolbar verbs go through the same controller. The controller validates each edit and then publishes the new document, geometry and selection together, so observers see one consistent update per edit. The canvas and its renderer follow the controller through lightweight internal notifiers; your own controls observe the same values through the public getters.

Rebuild the toolbar ​

Add flutter_mobx: ^2.4.0 to your app dependencies, run flutter pub get, and import package:flutter_mobx/flutter_mobx.dart. Replace buildToolbar() in the Column's children with:

dart
Observer(
  builder: (context) => buildToolbar(),
),

Inside buildToolbar, use the current history state:

dart
// Undo button:
onPressed: controller.canUndo ? () => showDecision(controller.edits.undo()) : null,
// Redo button:
onPressed: controller.canRedo ? () => showDecision(controller.edits.redo()) : null,

Reads of selection, activeTool, canUndo, canRedo, isInteracting, isAttached, document, scene, camera and elementById(id) on the controller, of controller.options<T>(), of document, scene, snapshot and bounds on controller.diagram, and of controller.effects.playing, inside an Observer are tracked. Only changes to the values that it reads rebuild it. Read those values during the builder's execution, not exclusively in a button's later onPressed callback. Nested child widgets need their own Observer when those children perform the reads.

There is no manual subscription or revision counter to dispose. Observer manages its own reaction; your screen still owns and disposes the controller after unmounting its canvas. Avoid wrapping the whole editor when a small toolbar or property control is sufficient.

Choose the right subscription ​

Your requirementSubscribe withRead
Toolbar availability, selection or active toolObserver / MobX reactionTracked controller getters
Save, audit or synchronize completed editscontroller.diagram.changes.addListenerchanges.value!.after, .elements
Zoom label or camera overlayObservercontroller.camera

Subscribe to one element ​

Read controller.elementById(id) inside an Observer or MobX reaction when a control depends on one shape or connector. Unlike reading controller.document, this does not rebuild the control when a different element changes. Removal returns null; Undo can restore the element and notify the same subscription.

Outside Flutter, reaction((_) => controller.elementById(id), ...) from package:mobx observes the same element; dispose the reaction when its consumer is done.

Document, scene, selection, tool, history, interaction, camera and options changes are observed separately, so a control that reads only the selection is not rebuilt when a shape moves. Built-in controls such as the inspector and diagnostics panel work the same way.

The controller's document and scene include its own in-progress preview, so observers of them also see interaction previews and provisional text edits; controller.diagram.document, scene and snapshot retain committed content while a gesture is previewing. A rebuild therefore does not mean there is a new document to save. Completed change events on diagram.changes exclude canceled gestures, selection and camera movement; any number of consumers can add and remove their own listeners.

Bring your own state management ​

A MobX reaction over the controller's getters, or a diagram.changes listener, can update a Riverpod provider, BLoC stream or your own observable projection. Keep document edits as controller verbs; do not maintain a second mutable diagram in your state store. Subscribe when the consumer attaches and unsubscribe when it detaches. If a widget accepts a replaceable controller, move subscriptions in didUpdateWidget as well.

Observers may read state and update host UI, but cannot synchronously issue another diagram edit during notification. Schedule any follow-up verb after delivery and re-read current state then.

Checkpoint: Undo disables after exhausting history, and Redo updates after canvas edits as well as toolbar clicks. No manual refresh call is needed.