Grids and snapping
GridExtension owns the grid: its GridOptions set the base interval, visibility, snapping and pattern. With the grid visible, direct manipulation snaps to the displayed spacing, including density reduction when zooming out. It never snaps to hidden subdivisions. With the grid hidden, snapping uses the configured base interval. Visibility does not enable snapping by itself.
On this page
| Category | What you can do |
|---|---|
| Grid options | Set visibility, spacing and snapping. |
| Patterns | Choose or compose a visual pattern. |
| Layers | Describe each pattern layer. |
Grid options
Core always installs GridExtension() (id vyuh.core.grid); installing your own replaces it with the values you give. Read the current options with controller.options<GridOptions>() and change them at runtime with controller.edits.setOptions(...), for every controller on the diagram. See extensions and options for how options work.
dart
GridExtension({
bool visible = false,
bool snap = false,
double size = 20,
DiagramGridPattern pattern = DiagramGridPattern.layered,
Iterable<DiagramGridPattern> patterns = DiagramGridPattern.presets,
})GridOptions field | Type | Default | Contract |
|---|---|---|---|
visible | bool | false | Paint the grid. Does not enable snapping. |
snap | bool | false | Enable grid snapping. Does not require a visible grid. |
size | double | 20 | World-space interval, normalized by GridExtension and copyWith. |
pattern | DiagramGridPattern | DiagramGridPattern.layered | Immutable composition of paint layers. |
patterns is the list the property panel offers; GridExtension contributes Show grid, Grid type, Snap to grid and Grid size to the inspector's canvas group. copyWith returns new options; omitted arguments preserve existing values.
dart
final controller = DiagramController(
configuration: DiagramConfiguration(
extensions: [GridExtension(visible: true, snap: true)],
),
);
final grid = controller.options<GridOptions>();
controller.edits.setOptions(grid.copyWith(size: 40));Size normalization
copyWith(size: value) normalizes the interval, and effectiveSize reports the normalized interval of any options value. Painting and snapping always use the normalized interval.
dart
final grid = const GridOptions().copyWith(size: 23);
assert(grid.size == 24);| Constraint | Value |
|---|---|
| Minimum interval | GridOptions.minimumSize, 4 |
| Maximum interval | GridOptions.maximumSize, 128 |
| Interval increment | GridOptions.sizeStep, 4 |
Finite inputs are divided by 4, rounded with Dart's round(), multiplied by 4, and clamped to [4, 128]. Non-finite inputs become 20. Normalization does not throw for those values.
DiagramGridPattern
dart
factory DiagramGridPattern({
required String id,
required String label,
required Iterable<DiagramGridLayer> layers,
})| Property | Type | Requirement |
|---|---|---|
id | String | Nonempty after trimming and no leading/trailing whitespace. |
label | String | Nonempty after trimming. |
layers | List<DiagramGridLayer> | Unmodifiable copy containing 1–16 layers. |
Patterns compare by id, label, and ordered layer values, with a matching hashCode.
| Static value | ID / label | Layers |
|---|---|---|
DiagramGridPattern.dots | dots / Dots | One default DiagramDotGridLayer. |
DiagramGridPattern.lines | lines / Lines | One default DiagramLineGridLayer. |
DiagramGridPattern.layered | layered / Layered | Lines at interval 1, color 0xB8E5E5E5; lines at interval 5, color 0x9ED4D4D4. |
DiagramGridPattern.presets | — | Constant list [dots, lines, layered]. |
Grid layers
DiagramGridLayer is sealed. Its concrete productions are dots, lines, and crosses:
dart
const DiagramDotGridLayer({
int spacingMultiple = 1,
DiagramColor color = const DiagramColor(0xFFCDD5E4),
double size = 2,
})
const DiagramLineGridLayer({
int spacingMultiple = 1,
DiagramColor color = const DiagramColor(0xFFCDD5E4),
double size = 1,
})
const DiagramCrossGridLayer({
int spacingMultiple = 1,
DiagramColor color = const DiagramColor(0xFFCDD5E4),
double size = 3,
})| Property | Type | Default | Meaning / accepted range in a pattern |
|---|---|---|---|
spacingMultiple | int | 1 | Multiplies the shared world interval; 1–1024. |
color | DiagramColor | DiagramColor(0xFFCDD5E4) | ARGB integer from 0 through 0xFFFFFFFF. |
size | double | Dot: 2; line: 1; cross: 3 | Screen-space dot diameter, line stroke width, or cross arm length. Must be finite, > 0, and <= 32. |
Layer values compare by concrete type and all three properties. Validation occurs when constructing DiagramGridPattern, which throws ArgumentError for invalid identity, label, layer count, spacing, size, or color. Layer constructors themselves do not perform this pattern validation.
Example
dart
final pattern = DiagramGridPattern(
id: 'cross-and-major-lines',
label: 'Crosses and major lines',
layers: const [
DiagramCrossGridLayer(size: 3),
DiagramLineGridLayer(spacingMultiple: 5, color: DiagramColor(0x99CDD5E4)),
],
);
controller.edits.setOptions(
controller.options<GridOptions>().copyWith(
visible: true,
snap: true,
size: 20,
pattern: pattern,
),
);To offer the pattern in the inspector, install GridExtension(patterns: [...DiagramGridPattern.presets, pattern]); the list is GridOptions.patterns, and duplicate pattern ids throw an ArgumentError. See custom grid patterns for a canvas example. Rotation snapping uses EditorOptions.rotationSnap, owned by EditorExtension(rotationSnap: ...), independent of the grid. EditorOptions implements the core DiagramSnapSettings interface the engine reads; without the editor the engine snaps rotation to 15° (DiagramSnapSettings.standard).
Neighbor snap guides
Grid snapping and neighbor alignment are independent. Install the optional vyuh_diagram_extension_snap_guides package to align moved selections to nearby resolved bounds:
dart
import 'package:vyuh_diagram_extension_editor/vyuh_diagram_extension_editor.dart';
import 'package:vyuh_diagram_extension_snap_guides/vyuh_diagram_extension_snap_guides.dart';
import 'package:vyuh_diagram_flutter/vyuh_diagram_flutter.dart';
final controller = DiagramController(
configuration: DiagramConfiguration(
extensions: [
EditorExtension(),
SnapGuidesExtension(
policy: const DiagramSnapGuidePolicy(
sides: true,
centers: true,
maxCandidates: 24,
),
tolerance: 6,
),
],
),
);SnapGuidesExtension (id vyuh.snap_guides) owns SnapGuidesOptions (enabled, policy, tolerance, theme, copyWith). Toggle guides at runtime:
dart
controller.edits.setOptions(
controller.options<SnapGuidesOptions>().copyWith(enabled: false),
);Guide lines use SnapGuidesOptions.theme, set with SnapGuidesExtension(theme: SnapGuideTheme(color: color)). Without one they use SnapGuideTheme.light (Color(0xFF5260FF)) or SnapGuideTheme.dark by the canvas diagram theme's brightness. The color does not change snapping behavior. The snap tolerance is in screen pixels. Candidate shapes must intersect the current viewport. Candidate limits range from 1 to 256 and apply after the spatial query and distance ranking. Guides use resolved bounds, including borders. Selected elements and their containing ancestors are excluded. Neighbor alignment takes priority over the grid on aligned axes; other axes keep grid behavior. Guides appear only during selection moves and clear on completion or cancellation.
Try snap guides and policy options.
Headless snapping
The same rules are plain Dart values, so controllers without a canvas snap exactly as the canvas does. DiagramGridSnapping.point in package:vyuh_diagram_core/adapter.dart snaps to the visible grid for a viewport. Neighbor alignment belongs to the snap guides package: SnapGuidesOptions is also a pure-Dart DiagramMoveAdjuster carrying the policy and tolerance that SnapGuidesExtension installs. Its adjust(DiagramMoveRequest) returns a DiagramMoveAdjustment with the settled delta and the DiagramMoveGuides that explain it.
Core offers only the generic hook. A Flutter extension contributes an adjuster by overriding DiagramFlutterExtension.createMoveAdjuster(controller); the canvas applies installed adjusters in order and exposes their guides as DiagramCanvasExtensionHost.moveGuides. The snap-guides extension paints those guides.
dart
import 'package:vyuh_diagram_flutter/adapter.dart';
import 'package:vyuh_diagram_flutter/vyuh_diagram_flutter.dart';
final snapped = DiagramGridSnapping.point(
point,
controller.options<GridOptions>(),
visibleBounds: viewportWorldBounds,
zoom: zoom,
);