Animation & effects
A diagram moves in three ways, and none of them is a document edit:
| Animation | API | What moves |
|---|---|---|
| Effects (this page) | controller.effects.play(effect, on: id) | A transient emphasis on one shape or connector: particles, pulses, halos, scaling and fading. |
| Camera motion | controller.view.show(target, motion: ...) | The viewport travels to a target. See Navigation and motion. |
| Element motion | move, resize, setElementBounds and layout with motion: | Elements travel to the geometry an edit gave them. See Animated geometry and Live data. |
Effects play on a mounted editing canvas. Playing, changing or stopping one never edits the document, adds undo entries, changes hit testing, or appears in exports.
dart
// Particles flowing along a connector.
final flow = controller.effects.play(
const Effect.flow(particle: EffectParticle.dash(), count: 6),
on: 'order-to-invoice',
);
// Blink a shape three times in the brand color, then stop.
final blink = controller.effects.play(
const Effect.pulse(
color: DiagramColor(0xFF4F46E5),
waveform: Waveform.step,
period: Duration(milliseconds: 500),
repeat: 3,
),
on: 'invoice',
);
await blink.done;
flow.stop();Loading interactive example…Open in new tab ↗
On this page
| Category | What you can do |
|---|---|
| The five kinds | Choose flow, pulse, halo, scale or fade. |
| Play, update and stop | Control effects with their handles. |
| Shared values | Timing, color, sizes and particles every kind reads. |
| Blink and pulse | Oscillate smoothly or switch on and off. |
| Repeat and hold | Play a number of cycles, or hold a fade. |
| Branding recipes | Effects in your brand colors. |
| Reduced motion | What plays when the platform asks for less motion. |
| Custom effects | Register your own kinds. |
The five kinds
| Kind | Plays on | Behavior |
|---|---|---|
Effect.flow() | Connectors | Particles or marching dashes travel along the route. The connector's own stroke dims to baseOpacity while it plays. |
Effect.pulse() | Shapes and connectors | Oscillates between rest and emphasis. target picks what changes: EffectTarget.fill (a tint over a shape's fill), .stroke (a bright outline or route), .opacity (the element's opacity, between 1 and dim) or .all. Waveform.step makes it blink. |
Effect.halo() | Shapes | A ring around the outline. HaloMode.ripple grows it spread outward and fades it each cycle; HaloMode.steady glows in place. |
Effect.scale() | Shapes | Grows the shape to amount times its size about its center and back, such as 1.08 to breathe or 1.2 to pop. |
Effect.fade() | Shapes and connectors | Changes opacity once over period: FadeDirection.fadeOut to dim, or FadeDirection.fadeIn from dim. It holds the result until stopped. |
Scale and fade present the element itself: its fill, border, text, ports, connector markers, labels and widget slots scale and fade together. Selection handles stay on the element's real bounds, and clicks land where the element really is, not where it appears while scaled.
Playing an effect on an element it does not suit throws an ArgumentError, such as a flow on a shape or a scale on a connector. Several effects may play on one element; their scales and opacities multiply.
Play, update and stop
dart
EffectHandle play(Effect effect, {required String on})
void stopAll({String? on})
List<EffectHandle> get playing
List<EffectHandle> playingOn(String id)
EffectRegistry get registry| Member | Contract |
|---|---|
play | Starts effect on the element with id on. Throws ArgumentError naming the first invalid value, for an unregistered kind, or for an element the kind does not suit. When the element does not exist, the returned handle is already stopped. Throws StateError while no editing canvas is mounted. |
stopAll | Stops every effect, or only those on the element with id on. |
playing, playingOn | Handles of the playing effects, observable in an Observer. |
registry | The kinds the canvas can play, including custom ones. |
EffectHandle member | Contract |
|---|---|
elementId | The element the effect plays on. |
effect | Current values, or null once stopped. |
isPlaying | Whether the effect still plays. |
update(effect) | Replaces the values and keeps the timeline, so a color change does not restart a cycle. Returns false once stopped. |
stop() | Stops the effect; stopping a fade restores the element's opacity. Safe to call more than once. |
done | Completes when the effect stops: stopped, finished its repeat cycles, lost its element, or its canvas unmounted. |
Effects follow their element as it moves, resizes or reroutes, including while element motion animates it. Removing the element stops its effects, and undoing the removal does not restart them.
Shared values
Every value has a default; each named constructor offers the values its kind reads. copyWith(...) changes any value (clearEndColor: true removes a gradient, clearRepeat: true repeats forever again).
| Value | Type | Default | Contract |
|---|---|---|---|
period | Duration | 1600 ms (fade 600 ms) | One cycle, or the whole change of a fade. Positive. |
delay | Duration | zero | Time before the effect starts, for staggering. A fade shows its start during the delay; other kinds show nothing. |
repeat | int? | null | Cycles before the effect stops itself; null repeats until stopped. At least 1. |
phase | double | 0 | Offset into the cycle, from 0 to 1, for staggering. |
reverse | bool | false | Runs each cycle backward, such as particles flowing from end to start. |
waveform | Waveform | .smooth | smooth, step or easeInOut; see blink and pulse. |
color | DiagramColor? | null | The effect color; null uses the connector color or shape stroke. |
endColor | DiagramColor? | null | Second color of a gradient across the effect. |
opacity | double | 0.9 | Peak opacity of what the effect draws. |
dim | double | 0.6 pulse, 0 fade | The element's opacity at rest for a pulse, and the faded end of a fade. |
baseOpacity | double | 0.2 | The connector's own stroke opacity while a flow or pulse plays along it. |
target | EffectTarget | .all | What a pulse changes. |
mode | HaloMode | .ripple | How a halo moves. |
direction | FadeDirection | .fadeOut | Which way a fade goes. |
amount | double | 1.08 | The size factor a scale reaches. Above 0, at most 10. |
width | double | 2 | Dash, pulse or halo stroke width. |
spread | double | 8 | How far a halo reaches beyond the outline. |
particle | EffectParticle | .circle() | The flow particle, below. |
count | int | 5 | Evenly spaced flow particles. |
size | double | 3 | Particle radius, half a character's font size, or an eighth of a dash. |
scale | EffectScale | .world | world sizes follow zoom; screen keeps size, width and spread in logical pixels. |
Values are checked when an effect plays or updates, in release builds too: controller.effects.play(const Effect.pulse(repeat: 0), on: id) throws an ArgumentError whose name is repeat. isValid and validate() run the same check.
Particles
| Constructor | Shape | Size |
|---|---|---|
EffectParticle.circle() | A filled circle. | Radius = size. |
EffectParticle.dash() | A stroke that follows the route: marching dashes. | Length = size × 8; thickness = width. |
EffectParticle.arrow() | A chevron that points the way it travels. | Half-length = size. |
EffectParticle.path(points) | A filled outline through points, in particle units with x pointing the way it travels. | Units × size. |
EffectParticle.polygon({sides = 4, rotation}) | A regular polygon, 3 to 20 sides. | Circumradius = size. |
EffectParticle.character(character) | One grapheme, such as an emoji. | Font size = size × 2. |
Arrows and path particles turn with the route. count changes spacing, not size; dense particles may overlap.
Blink and pulse
A pulse moves between rest (its element at dim opacity, nothing drawn) and emphasis (full opacity, tinted fill and bright stroke) once per period. The waveform decides how:
| Waveform | Over one cycle |
|---|---|
Waveform.smooth | Eases from rest to emphasis at mid-cycle and back, like a sine. |
Waveform.step | Emphasis for the first half of the cycle, rest for the second: a blink. |
Waveform.easeInOut | Rises and falls along MotionCurve.easeInOut, holding briefly at each end. |
dart
// A calm pulse on a connector's route.
controller.effects.play(
const Effect.pulse(target: EffectTarget.stroke, width: 4),
on: 'feed',
);
// An urgent blink of a shape's fill, twice a second.
controller.effects.play(
const Effect.pulse(
target: EffectTarget.fill,
waveform: Waveform.step,
period: Duration(milliseconds: 500),
color: DiagramColor(0xFFE11D48),
),
on: 'alarm',
);The same waveforms shape scale and a steady halo: Effect.scale(waveform: Waveform.step) switches between sizes. A fade uses the waveform as its easing.
Repeat and hold
repeat: n plays n cycles, then the effect stops itself, its handle stops playing and done completes:
dart
final attention = controller.effects.play(
const Effect.scale(
amount: 1.2,
period: Duration(milliseconds: 360),
waveform: Waveform.easeInOut,
repeat: 1,
),
on: 'new-order',
);
await attention.done; // After one pop.A fade plays once and holds its end state for as long as it plays, without redrawing. Stop it to restore the element:
dart
// Dim everything except the path under review.
final dims = [
for (final id in unrelatedIds)
controller.effects.play(const Effect.fade(dim: 0.25), on: id),
];
// Later:
for (final dim in dims) {
dim.stop();
}Stagger several effects with delay or phase:
dart
for (final (index, id) in steps.indexed) {
controller.effects.play(
Effect.halo(delay: Duration(milliseconds: 200 * index), repeat: 1),
on: id,
);
}Branding recipes
Every drawing kind takes color and endColor, so effects can carry a brand palette:
dart
const brand = DiagramColor(0xFF4F46E5);
const accent = DiagramColor(0xFFF97316);
// A gradient flow from the brand color to the accent.
controller.effects.play(
const Effect.flow(color: brand, endColor: accent, count: 8),
on: 'pipeline',
);
// A steady brand halo on the selected step.
controller.effects.play(
const Effect.halo(mode: HaloMode.steady, color: brand, spread: 10),
on: 'current-step',
);
// Fixed-size arrows that read at every zoom.
controller.effects.play(
const Effect.flow(
particle: EffectParticle.arrow(),
color: brand,
size: 6,
scale: EffectScale.screen,
),
on: 'handoff',
);Colors adapt to the canvas theme like authored colors do. The connector effects and shape effects examples offer a brand color picker for every kind.
Reduced motion
When the platform asks to reduce motion (MediaQuery.disableAnimations), effects do not move. Each holds a static emphasis instead: a pulse or scale at its peak, a rippling halo half spread, flow particles still, and a fade at its end. Effects with a repeat count still stop after their cycles, so done completes as it would otherwise.
Custom effects
Register an EffectDefinition through a Flutter extension's effects. The definition is in package:vyuh_diagram_flutter/extension_api.dart; Effect, EffectKind and EffectPlacement are in the main library.
dart
import 'dart:math' as math;
import 'package:vyuh_diagram_flutter/extension_api.dart';
import 'package:vyuh_diagram_flutter/vyuh_diagram_flutter.dart';
const orbit = EffectKind('app.orbit');
final orbitEffects = DiagramFlutterExtension(
id: 'app.orbit',
effects: [
EffectDefinition(
kind: orbit,
label: 'Orbit',
placement: EffectPlacement.around,
properties: const {EffectProperty.color, EffectProperty.speed},
paint: (canvas, context) {
final bounds = context.shape.bounds;
final radius = math.max(bounds.width, bounds.height) / 2 + 8;
canvas.drawArc(
Rect.fromCircle(
center: Offset(bounds.center.x, bounds.center.y),
radius: radius,
),
context.progress * math.pi * 2,
math.pi / 2,
false,
context.createPaint()..style = PaintingStyle.stroke,
);
},
),
],
);
final controller = DiagramController(
configuration: DiagramConfiguration(extensions: [orbitEffects]),
);
DiagramCanvas(controller: controller);
controller.effects.play(const Effect(orbit), on: 'gateway');See the custom effect example for a complete dashed ring that the inspector edits.
| Placement | Plays on | Painting |
|---|---|---|
EffectPlacement.along | Connectors | Along the route; badge labels stay readable above it. |
EffectPlacement.inside | Shapes | Clipped to the shape's outline. |
EffectPlacement.around | Shapes | Over the shape and beyond its outline, for halos, rings and badges. |
EffectPlacement.any | Shapes and connectors | Unclipped; read context.element to tell them apart. |
EffectDefinition
| Argument | Type | Default |
|---|---|---|
kind | EffectKind | Required, unique. |
label | String | Required inspector label. |
placement | EffectPlacement | Required. |
paint | void Function(Canvas, EffectContext)? | Draws over the canvas each frame, in world coordinates. Paint only; change content through controller verbs. |
present | EffectPresentation Function(EffectContext)? | Scales and fades the element's own rendering, as scale and fade do. A definition paints, presents, or both. |
animated | bool | true. false for effects that change only when updated, so the canvas does not redraw them each frame. |
holds | bool | false. true plays once over the period and holds the end, like a fade. |
properties | Set<EffectProperty> | Empty. Values an inspector offers. |
editing | EffectEditing | Inspector ranges, below. |
EffectPresentation(scale:, opacity:) scales about the element's center and multiplies its opacity. Connectors ignore scale.
EffectContext
| Member | Type | Purpose |
|---|---|---|
element | ResolvedElement | The connector or shape. |
connector, shape | ResolvedConnector, ResolvedShape | The element as its kind; the other throws StateError. |
effect | Effect | Current values. |
progress | double | Position in the cycle, from 0 to 1, after phase and reverse; for a holding effect, how far its change has come. |
level | double | Emphasis from 0 (rest) to 1 (peak): progress shaped by the waveform. Held at 1 with reduced motion. |
elapsed | Duration | Time since the effect started, after its delay. |
path | Flutter Path | Copy of the connector route or the shape outline, in world coordinates. |
metric | Flutter PathMetric | Length and segments of a connector route. |
pathGeometry | ResolvedPathGeometry | Route sampling with tangents, for connector effects. |
inverseZoom, sizeToWorld | double | Inverse camera zoom; sizeToWorld is 1 for world scale and the inverse zoom for screen scale. |
createPaint([intensity = 1]) | Flutter Paint | The effect color, gradient and opacity, adapted to the canvas theme; intensity from 0 to 1. |
adapt | ColorAdaptation | How the canvas theme adapts authored colors. Apply it to any other color the effect draws. |
Registry and inspector ranges
| API | Contract |
|---|---|
EffectRegistry.standard | Flow, pulse, halo, scale and fade. |
EffectRegistry(definitions) | Exactly the supplied effects; include the built-ins with [...EffectRegistry.standard.definitions, ...custom]. Rejects empty kinds or labels, duplicate kinds, and definitions that neither paint nor present, with ArgumentError. |
definitions, forConnectors, forShapes | All definitions, or those that play on connectors or on shapes. |
contains(kind), registry[kind], require(kind) | Lookup; require throws StateError when missing. |
The inspector offers the advertised EffectProperty values of the effect on the selected shape or connector: particle, color, gradient, speed, baseOpacity, opacity, size, width, count, direction, scale, waveform, repeat, delay, target, mode, amount, spread, dim and fade. EffectEditing groups their EffectRange(minimum, maximum, step) bounds; ranges do not clamp authored values.
| Property | Default range | Units |
|---|---|---|
speed | (0.1, 5, 0.1) | Cycles per second |
delay | (0, 10, 0.1) | Seconds |
baseOpacity, opacity, dim | (0, 100, 5) | Percent |
size, width | (1, 24, 1) | Pixels |
spread | (1, 48, 1) | Pixels |
amount | (0.5, 2, 0.02) | Scale factor |
count | (1, 30, 1) | Particles; whole numbers |
repeat | (1, 20, 1) | Cycles; whole numbers |
Examples
| Example | Shows |
|---|---|
| Connector effects | Dots, dashes, arrows and gradient flows; pulse and blink along connectors. |
| Shape effects | Pulse, blink, ripple and steady halos, breathe and pop, fades and dimming, in brand colors. |
| Element motion | Animated move, resize and layout. |
| Custom effect | A registered effect kind the inspector edits. |
| Navigation and animation | Camera motion. |
| Live energy flow | Live data with flows, a beacon and animated load bars. |