NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #4076 most downloaded on pub.dev
Smooth, sliver-based drag & resize grid engine. Supports nested grids, cross-sliver DND, responsive layouts. Native Flutter alternative to GridStack & react-grid-layout.
Last release 17 days ago
21 Sep 2026
Release timing varies
gaps range from 8 days to 3 months
Nearly every release is documented
notes for 38 of 38 stable releases
Nothing withdrawn
no release was ever pulled
10 months old
38 releases · first in 2025
One column per month.
Collision resolution is 2.2× to 6× faster on the drag and resize paths, the largest factors on grids containing a tall tile. Layout output is byte-ide
optimizeLayout are unchanged: they do not go through the collision
index, and neither do small grids nor gestures with short cascades; they were never an important cost.Per-operation before/after, end-to-end frame times and the WebAssembly comparison are in BENCHMARK.md.
Fixed a tile destroying the state of its content whenever its chrome changed. Most visible
with nested grids, where it tore down the embedded NestedDashboard mid-gesture.
Fixed DashboardOverlay remounting its child when the background appears or disappears
(gridStyle: isEditing ? const GridStyle() : null), which reset the scroll offset and could throw
ScrollController attached to multiple scroll views. Dashboard and NestedDashboard are not
affected — their gridStyle is never null.
For 99% of users, this update is a seamless drop-in with zero code changes required.
Container that carried the decoration is
replaced by a DecoratedBox and a Padding that are always present. Nothing changes visually, but
a widget or golden test matching on a tile's internal structure may need updating — in particular,
that Container no longer exists.@visibleForTesting counters and a reset helper are now exported, for applications that
want to assert these invariants in their own suites: debugRowIndexQueries,
debugRowIndexRowVisits and debugResetRowIndexCounters() (collision-probe scan range — see
BENCHMARK.md), and debugOverlayDisposedDuringInteraction (an overlay destroyed
while it was driving a gesture).> Note: For 99% of users, this update is a seamless drop-in with zero code changes required.
Note: For 99% of users, this update is a seamless drop-in with zero code changes required.
DashboardController Interface: New members (fluidResize, setFluidResize()) were added to
the abstract interface. Only applications creating custom classes
with class MyController implements DashboardController (e.g. manual test mocks) need to declare the missing members.Dashboard.fluidResize, NestedDashboard.fluidResize, controller.setFluidResize()):
Opt-in pixel-tracking resize preview.
Disabled by default to avoid pointer-frequency rebuilds with itemLayoutBuilder; cached builders are unaffected.
The tile follows the handle in raw pixels, clamped to constraints and obstacles, while its slot displays
the snap-target placeholder, neighbors reflow against the projected slot, and the tile animates into position on release.Dashboard.resizeSettleDuration / DashboardOverlay.resizeSettleDuration:
Configurable duration for the snap-to-slot release animation (defaults to 120 ms; Duration.zero to snap immediately).gridCellRect() and SlotMetrics.strideX / strideY / cellRect(): Unified tile geometry helpers ensuring consistency across layout passes and feedback overlays.mainAxisSpacing != crossAxisSpacing.isResizing state remaining latched after tapping a resize handle with DragStartGesture.tap without moving.> Note: For 99% of users, this update is a seamless drop-in with zero code changes required.
Note: For 99% of users, this update is a seamless drop-in with zero code changes required.
onLayoutChanged, onUndo, onRedo, onWillUndo,
and onWillRedo now provide a List<LayoutItem>.unmodifiable instead of the controller's
internal mutable layout.value reference. This protects internal layout state and
ID ordering invariants against accidental in-place mutations.
onLayoutChanged (items.sort(...)), call items.toList() first. Reading/iterating the list is completely unaffected.DashboardController Interface: New members were added to the abstract interface.
Only applications creating custom classes with class MyController implements DashboardController
(e.g. manual test mocks) need to add the missing members.Rectangle / Lasso Selection (Desktop & Web): On desktop and web, drag over empty grid
space to select every tile the rectangle overlaps. Selection previews live,
replaces the current selection by default, and is additive while a
multiSelectKeys key is held. Edge auto-scroll and mouse wheel are
supported.
LassoStyle (mode, fillColor, borderColor, borderWidth) and
LassoSelectionMode (emptySpace, modifierRequired, disabled), with
LassoStyle.byDefault and LassoStyle.off.DashboardController.lassoStyle and lassoModifierHeld.DashboardShortcuts.lassoModifier (defaults to Shift).DashboardGuidance.lassoSelect, a11yLassoStart, a11yLassoEnd, and the
A11yCountMessageBuilder typedef.Swap Drag Mode: A dragged tile can trade places with the tile it lands
on (> 50% coverage) instead of pushing neighbours away. Off by default — DragMode.cascade
reproduces the previous behaviour exactly.
DragMode (cascade, swap) and the pure engine helper swapElements.DashboardController.dragMode, setDragMode(),
getEffectiveDragMode({bool? modifierHeld}) and swapModifierHeld.DashboardShortcuts.swapModeModifier (defaults to Shift) to dynamically toggle
the opposite mode while held.Per-Grid Drop Rules for Nested Grids: canAcceptItem on
DashboardNestedScope decides which items each grid of a tree accepts. A
refused grid is transparent, passing the drag through to the parent grid.
DashboardCanAcceptItemCallback(item, targetGrid, sourceGrid).Payload-Aware External Drops: Added DashboardExternalTemplateBuilder<T> on Dashboard,
DashboardOverlay, and NestedDashboard. Allows external draggable payloads (DragTarget)
to define their intrinsic footprint (w, h), resize constraints (minW/maxW/minH/maxH),
flags (isSectionBarrier, isStatic, isResizable), and extra metadata during drag-over
and upon drop.
Native Keyboard Shortcuts & A11y Actions:
Delete / Backspace (DashboardDeleteItemIntent) to remove selected items (respecting onWillDelete / onItemsDeleted).Ctrl+A / Cmd+A (DashboardSelectAllIntent) to select all editable items.Ctrl+D / Cmd+D (DashboardDuplicateItemIntent) to duplicate selected items.Ctrl+Z / Cmd+Z (DashboardUndoIntent) & Ctrl+Y / Cmd+Shift+Z (DashboardRedoIntent) to navigate layout history.Escape (DashboardCancelInteractionIntent) to clear active selection when idle.a11yDelete, a11ySelectAll, and a11yDuplicate on DashboardGuidance.Added copyWith, ==, and hashCode to GridStyle.
moveElement / moveCluster
with CompactType.horizontal and preventCollision: false could
return overlapping tiles due to bounded-axis wrapping.resolveCollisions pushing
obstructed items beyond the grid boundary; items now correctly wrap to
the next row matching _compactItemHorizontal.
Delete and keyboard shortcuts to target the previously focused tile.CompactType.none) compaction paths remain completely untouched.Native Undo / Redo layout history (`DashboardController`). The controller now keeps a transactional history of the spatial layout, backed by state_bea
No API breaking changes.
DashboardController). The controller
now keeps a transactional history of the spatial layout, backed by
state_beacon's UndoRedoBeacon.
canUndo, canRedo — bind a button's enabled state
directly with controller.canUndo.watch(context).undo(), redo() (both Future<bool>), clearHistory(),
setMaxHistoryLength(int) (default 30, throws ArgumentError when
negative), and the maxHistoryLength getter.maxHistoryLength. Pass 0 to opt out
entirely: no history beacon is created, no snapshot is copied and no
removed item is retained, so applications that do not want undo/redo pay
nothing. Can also be flipped at runtime with setMaxHistoryLength(0).onUndo, onRedo
(DashboardHistoryRestoreListener) and the veto hooks onWillUndo,
onWillRedo (DashboardHistoryVeto, may be asynchronous).undo() / redo() emit onLayoutChanged before their own callback, so
existing auto-save mechanisms keep working unchanged.LayoutItem
instances, extra metadata included, and carry the column count they were
taken under.displacedColor and displacedDecoration to DashboardItemStyle. Secondary items displaced by push cascades during an active drag interaction now render a visual highlight, clearing automatically upon drop.displacedItemColor to MinimapStyle. Displaced items during an active drag cascade are rendered in a dedicated batched path on the minimap.Alt / Option + drag): Hold Alt / Option when dragging a tile to pull a copy out while leaving the original in place.
onCloneRequested (DashboardCloneRequestCallback) to Dashboard, DashboardOverlay, NestedDashboard, and DashboardNestedScope. Return a cloned LayoutItem with a new ID, or null to degrade to a plain move.DashboardShortcuts.cloneKeys (defaults to altLeft / altRight).onCloneRequested is provided. Alt+click without drag does not trigger duplication or history recording.setSlotCount (responsive breakpoints), metadata-only
updateItem(recompact: false) and cross-grid drops are deliberately not
history boundaries. See the README table for the full list.undo() / redo() return false while a drag or resize gesture is in
flight: gestures are atomic transactions.DashboardController is an abstract interface and gained 11 members. Code
that implements DashboardController by hand must be updated; mocktail
mocks are unaffected.addItem is itself a transactional boundary. Undoing the
gesture therefore takes two undo() calls: the first returns the copy to the
cell it appeared on, the second removes it.Pixel-Preserving Dimension Projection (`preservePixelSize`): Added DimensionProjectionPolicy.preservePixelSize to preserve an item's exact physical pi
No API breaking changes.
Pixel-Preserving Dimension Projection (preservePixelSize):
Added DimensionProjectionPolicy.preservePixelSize to preserve an item's exact physical
pixel dimensions across grids of different densities or container widths (e.g. dragging
tiles into narrow nested panels). Includes the programmatic
utility coordinator.projectItemBetween.
Closed hosts & custom drop targets (onItemDroppedOnHost): a tile can
now receive dropped items WITHOUT rendering a nested grid — closed folder
icons, archive tiles, compact badges.
LayoutItem.isDropTarget,
or when hasNestedGrid: true while its child grid is NOT mounted (closed folder).
Hosts with a mounted child grid keep regular cross-grid entry.DashboardItemStyle.nestTargetColor /
nestTargetDecoration). On release, the pre-drag layout is restored (nothing lands
on the grid) and onItemDroppedOnHost runs with (draggedItems, host, hostGrid, sourceGrid).Dashboard/DashboardOverlay (no nesting required) with
DashboardNestedScope.onItemDroppedOnHost as a scope-wide default. Zero overhead when
no callback is registered.Example & Documentation: upgraded nested_example.dart to demonstrate
size-driven collapsible folders (itemBreakpointBuilder + _HostSize.mini +
onItemDroppedOnHost). Shrinking a folder to 1 slot collapses it into an
interactive drop-target icon; dropping notes onto it files them away, and
enlarging reopens the nested grid with contents intact.
Added tests.
NestedDashboard could trigger a premature cross-grid exit session and move the parent host tile.padding.top in padded/nested grids after slot-count updates due to a stale render object reference in _findRenderSliver.performLayout and paint passes by storing paint offsets as primitive doubles (paintOffsetX/paintOffsetY), caching BoxConstraints, and caching ValueKey instances across geometric reorders.animateReflow toggles from property assignment order in updateRenderObject, ensuring reflow transitions seed reliably when enabled alongside a layout mutation.sliverKey Isolation: Enforced strict sliverKey scoping in DashboardGrid without unscoped fallbacks, preventing background grids from painting foreign origins in multi-sliver layouts.updateItem and replaceItem.Sequential subGridDynamic arming no longer strands converted hosts: hovering host A then host B during one drag silently overwrote the pending nest re
Empty-slot interactivity (Dashboard.onSlotTap / onSlotLongPress): tap or long-press an empty grid slot to receive its (x, y) coordinates — the "add a
No API breaking changes.
Dashboard.onSlotTap / onSlotLongPress):
tap or long-press an empty grid slot to receive its (x, y) coordinates —
the "add a widget here" hook for dashboard builders and no-code editors.
Fires only inside the grid's own bounds (several grids sharing a scroll
view never cross-fire); with fillViewport: true (the single-grid
default) the empty area below the content is tappable too. Zero cost
outside the tap event itself.LayoutItem.extra, Map<String, dynamic>?): carry business data or any
JSON-serializable configuration on the item itself. Travels through toMap/fromMap, so exportLayout/exportNestedTree persist layout
and configuration in one payload. Compared with shallow map equality —
replace the map, do not mutate it — and included in the content
signature, so changing it rebuilds the item's cached widget. Never
consulted during drags.DashboardController.maxRows, setMaxRows): bound
the grid to N rows (or columns when scrolling horizontally) for fixed
surfaces (TV, kiosk, A4 report). Enforced on drag targets, interactive
and programmatic resizes, and new-item auto placement — which falls back
below the cap rather than losing items when the bounded area is full.
Collision pushes are deliberately not truncated. Null (default) keeps
the classic unbounded grid.maxRows) is appended below the content instead of
being silently lost.onSlotTap handler) are clamped into the
columns and under maxRows — a 2-wide add on the last column no longer
overflows the grid, and a tap-add on the trailing row no longer grows a
capped grid.meta dependency version constraint to >=1.8.0 <2.0.0
for maximum Flutter SDK compatibility across 3.x releases.README Badges: Added dynamic status badges highlighting pub.dev version, quality score, and monthly downloads.
This milestone release introduces internal optimizations to the core layout engine under high-density scenarios, updates the benchmarking suite, and b
No API breaking changes.
This milestone release introduces internal optimizations to the core layout engine under high-density scenarios, updates the benchmarking suite, and brings the codebase to 100% test coverage.
(y << 20) | x integer set, bringing previous O(N) and O(N^2) linear searches down to O(1) spatial lookups.test/benchmark/benchmark.dart) using median and best-run metrics across batches to eliminate OS scheduling noise.Engine optimizations: for faster resize on large grids.
Widget minimap markers (DashboardMinimap.markerBuilder, opt-in): build any widget (an Icon, a badge, an animated indicator) positioned over an item's
DashboardMinimap.markerBuilder, opt-in):
build any widget (an Icon, a badge, an animated indicator) positioned
over an item's minimap rectangle; return null to skip an item. The
batched-Path markers layer remains the default and the recommended
choice for large sets (zero objects per marker, never re-rasterizes on
scroll); the widget layer mounts one Element/RenderObject per marker and
is re-reconciled on every minimap rebuild — imperceptible below ~50
markers, measurable at 500+.DashboardMinimap.onItemTap): tap an item's
rectangle to get the LayoutItem (select it, scroll to it…). When an
item is hit the default tap-to-scroll is suppressed for that tap; empty
areas still scroll.Reduced gif size for pub dev
Reduced gif size for pub dev
Fix README.md gif
Fix README.md gif
No API breaking changes. All new parameters are optional with defaults preserving 1.x behavior; without a DashboardNestedScope in the tree, the new co…
Sliver Dashboard 2.0 turns the single grid into a multi-grid system: dashboards nest inside items, and items drag freely across grids — nested, siblings, or independent slivers sharing one scroll view — with dimension projection between different column counts. Tiles can slide with GPU-only reflow animations (opt-in), the minimap gains overlay markers, multiple viewport indicators and pixel-exact scroll mapping, and desktop interactions get O(1) hover resolution with jitter-free nest targeting. The whole tree saves and restores in one call.
No API breaking changes. All new parameters are optional with defaults
preserving 1.x behavior; without a DashboardNestedScope in the tree, the
new code paths reduce to a few null-checks per pointer event.
Behavioral changes (corrections of 1.x defects that are observable):
fillViewport filler now render correctly instead
of drawing the indicator at the wrong place/size. Override with
DashboardMinimap.mainAxisLeadingExtent / mainAxisContentExtent.CompactType.none and custom compactors is unchanged.NestedDashboard: embed a full dashboard inside a grid item at any depth.DashboardNestedScope.
Live push-preview placeholder in whichever grid is hovered, floating drag
proxy (honors itemFeedbackBuilder), auto-scroll on the hovered grid,
constraint/flag/id preservation, and pre-drag restore when a drop lands on
no grid. Exactly one onLayoutChanged per affected grid, emitted at drop
time (never mid-gesture). The source grid keeps its geometry frozen (a
hole where the item left) for the whole session, and approaching an item
that already hosts a child grid freezes the collision pushes — so a
target grid never shifts away from the pointer mid-drag.DashboardNestedScope: opt-in coordinator scope with
onItemMovedToGrid, subGridDynamic, nestHoverDelay,
onNestedGridRequested.autoSlotCount: the nested grid's slot
count follows its host item width, keeping inner/outer cells visually
consistent during host resizes.sizeToContent (+ sizeToContentMax, chromeExtent): the host item
auto-grows/shrinks so the nested grid never scrolls internally. Manual
host resizes are not fought mid-gesture (the content-driven height sync
pauses during drag and resize, on both the parent and the child grid),
and background grid lines are painted only for the rows the content
occupies (new Dashboard.fillViewport param, default true, set to
false by NestedDashboard under sizeToContent).subGridDynamic: holding a dragged item over a plain item highlights it
and fires onNestedGridRequested after nestHoverDelay, letting the app
convert it into a nested grid.exportNestedTree / loadNestedTree save and
restore the whole tree in one call — robust to sliver virtualization
(parent links persist while a host item is scrolled out of view, so its
subtree still exports; call coordinator.unlinkChildGrid when removing a
nested grid permanently) (subGrid: {slotCount, items} payloads,
delivered automatically to grids that mount later).DashboardNestedCoordinator.moveItemToGrid.maxNestingDepth (DashboardNestedScope / DashboardNestedCoordinator,
default null = unlimited): caps how many nesting levels users can create.
The root grid is level 0, so 1 allows one level of nesting and 0
disables it. Enforced where levels are created — cross-grid drops of a
host item, and subGridDynamic arming — while plain leaf moves and explicit
moveItemToGrid calls are unaffected. canHostAtDepth(depth) exposes the
same predicate for building UI affordances.CrossGridProbe (DashboardNestedScope.probe): choose whether the
pointer (default) or the dragged tile's visual center decides which grid it
enters — the latter makes enter-vs-push independent of where the tile was
grabbed.sizeToContent nested grids) forward edge auto-scroll to their parent
grid, recursively — dragging a child tile downward grows the host and
scrolls the parent to follow.Dashboard parameters: crossGridDragOut,
acceptCrossGridItems (both default true, only meaningful inside a
scope).LayoutItem.hasNestedGrid (default false): declarative flag marking
items that host a nested dashboard. Lets a shared itemBuilder branch
generically (if (item.hasNestedGrid) return NestedDashboard(...)) — which
makes whole groups portable between grids without id coupling — travels
through plain exportLayout/importLayout round-trips, is targetable by
DashboardPolicy, and is included in contentSignature (toggling it
invalidates the cached item widget). The nested codec self-heals it: linked
hosts export with the flag set, and items carrying a subGrid payload are
normalized to hasNestedGrid: true on import. subGridDynamic skips
arming on flagged items even when their grid was never linked in the
session.DashboardController.updateItem(id, transform, {recompact}): safe,
controller-owned single-item mutation (flags, title, constraints, size),
replacing hand-written layout.value rewrites. No-op on unknown id or an
equal result, id changes are rejected (assert in debug, defensively
restored in release), transformed geometry is passed through bound
correction so invalid w/h/position cannot corrupt the layout, and
exactly one onLayoutChanged fires per effective change.
recompact: false skips pulling items back for metadata-only edits.DashboardController.replaceItem: A new public API to swap/replace an existing
grid item in-place. Automatically enforces ascending ID order, corrects bounds,
and performs write-through updates to active pre-drag snapshots. Designed to support
clean dynamic nested grid group/folder conversions on hover.movedAway / returned / canceled),
template-preserving external drop (onDropExternalItem), programmatic
clamped resize (setItemSize).SliverDashboard widgets separated by other native slivers (e.g.
SliverAppBar, SliverList) within the same DashboardNestedScope.
Containment is sliver-precise and allocation-free; dragging into empty
space outside the sliver opens a session behind an exit hysteresis and
never shadows the trash flow. In multi-sliver trees, each overlay needs a
sliverKey, an explicit controller, and a padding matching the
surrounding SliverPadding.projectionPolicy with three modes:
preserveLogicalSize: Keeps the exact original grid dimensions (e.g. 2x2 remains 2x2).preserveVisualProportion: Automatically scales tile dimensions proportionally based on the column count ratio between the source and target grids (e.g., a w:2 item in an 8-col grid scales down to w:1 in a 4-col grid, keeping its visual 25% width ratio).custom: Delegates the scaling arithmetic to a developer-defined customProjectionCallback.
Projection output is always sanitized against the target grid — including
custom callback output — and memoized per session.controller parameter to
SliverDashboard and sliverKey to DashboardOverlay to safely support
multiple concurrent sliver grids in the same viewport without context
collision or shadowing.animateReflow, default false):
pushed/compacted tiles slide smoothly (150 ms, reflowDuration)
to their new slot during drags and resizes. Layout stays instantaneous and
deterministic — only the painted offset interpolates, translating each
tile's cached RepaintBoundary layer on the GPU: zero widget rebuilds,
zero re-rasterization, zero allocation per paint pass. Slot-metric changes
(viewport resize, breakpoints, slot count) snap by design; hit-testing and
semantics use the final position immediately.precedingScrollExtent, scroll extent and live slot sizes onto the
controller at each layout pass; DashboardMinimap uses them to map its
viewport indicator and tap-to-scroll pixel-exactly (see Behavioral
changes), with mainAxisLeadingExtent / mainAxisContentExtent
overrides.DashboardMinimap.markers): custom overlay markers
(status dots, badges — circle/square/diamond/triangle, per-item alignment)
rendered in a dedicated cached layer. All markers are batched into one
Path per distinct color; the layer re-rasterizes only when the marker
list changes by value, never on scroll. A marker's size is a maximum:
it is capped to its tile's minimap rectangle.DashboardMinimap.viewportIndicators):
several ViewportIndicators (one per scroll segment / sibling grid), each
with its own segment mapping (mainAxisLeadingExtent /
mainAxisContentExtent) and style overrides. Painted by the single
scroll-bound viewport layer. For a single grid, omit this and let the
default indicator map itself automatically.itemAtGlobal resolves through an O(1)
coordinate-bucket index above 16 items (identity-cached per layout list
instance) instead of an O(N) scan per pointer event.hoverJitterTolerance, default 4 px): low-pass on
nest-hover host switching — sub-tolerance pointer noise at tile borders no
longer flickers the highlight or restarts the nest-hover timer.DragTarget size (placeholderWidth/Height) and could resurrect a
placeholder from a stale position; it now uses the hovering item's real
size for cross-grid drags and only re-anchors when a hover is actually
active.DashboardPolicy rules (specifically custom canCollide exclusions),
causing protected items (like folders/panels) to be pushed during external
or cross-grid hover events.MinimapStyle now implements value equality (==/hashCode), so painter
shouldRepaint short-circuits even when a style is constructed inline.README_NESTED_GRID.md: feature guide.example/lib/nested_example.dart, example/lib/multi_sliver_crossdrag_example.dart,
and a demo launcher in example/lib/main.dart.No breaking changes in this release.
No breaking changes in this release.
Easily create per-item breakpoints. The grid already computes every item's pixel size and slotCount during layout, so it now hands them to your builder directly — no extra layout passes.
DashboardItemBuilder execution remains unaffected by resize events.Interactive Section Barriers: Section barriers can now be dragged and rearranged in edit mode to easily organize layout sections.
No breaking changes in this release.
No breaking changes in this release.
activeColor to customize the border outline color when an item is actively being dragged.moveElement using a monotonic cascade paired with O(N*k) row-indexed queries, replacing the unconditional O(N^2) verification pass on every frame.FastVerticalCompactor._getOrUpdateKeyToIndex with an allocation-free string-identity sequence check during active drags.Float64List scratch buffer in performLayout to eliminate Rect allocations and reduce minor-GC pressure.RepaintBoundary and painting viewport changes on scroll notifications), reducing canvas repaint draw calls.SlotMetrics to prevent redundant background grid painting.KeepAlive support on active elements during drags to prevent unnecessary widget lifecycle churn.Stopwatch trailing-edge throttle to safely capture late-arriving pointer positions without overloading the browser thread.scrollToItem to prevent Future deadlocks when the controller is detached from an active overlay.correctBounds with safeguards for zero/negative coordinates and assertions for breakpoint mismatches.sortLayoutItems and Skyline compactors to ensure deterministic placement on multi-collision overlaps.DashboardPolicy API: Introduced a declarative interaction policy interface (DashboardPolicy). You can now intercept, validate, and block drag/resize s
DashboardPolicy API: Introduced a declarative interaction policy interface (DashboardPolicy). You can now intercept, validate, and block drag/resize starts, coordinate moves, or granular item-to-item collisions (e.g. blocking charts from pushing KPIs) on-the-fly without having to write a full custom compaction delegate.
Visual Section Barriers & Segmented Grids: Added support for dividing a single dashboard grid into distinct, organized sections. LayoutItem now supports the isSectionBarrier flag and sectionTitle property. When enabled, the item behaves as an immoveable horizontal divider and renders a section header using either a default style or a fully customizable sectionHeaderBuilder callback on Dashboard / SliverDashboard.
Auto-Shrink on Drag: Introduced adaptive neighbor shrinkage during drag operations. When moving items, enabling allowAutoShrink via setAllowAutoShrink(allow: true) on the controller allows the engine to dynamically contract neighboring items' heights down to their minH limits to make room for the moving item, minimizing vertical layout shifts and offering smoother, more cohesive grid reflows.
Unified Interactive Playground: You can now test all major capabilities (on Web, Desktop, and Mobile), including standard grid vs direct sliver composition, visual section barriers, adaptive neighbor shrinking, live JSON schema export/import editing, custom drag handles, and interactive mini-map scrubbing.
FastHorizontalCompactor and FastVerticalCompactor where widgets exceeding standard slot boundaries along the infinite scroll axis were incorrectly forced to index 0 and overlapped. The compaction engines now dynamically calculate their internal Skyline tide tracking arrays based on the actual physical bounds of the layout, fully supporting infinite rows and columns.No breaking change in this release.
Customizable Auto-Placement: Added support for dense, gap-filling item placement.
AutoPlacementStrategy enum with two strategies:
appendBottom: The classic behavior, appending new items below the existing layout.firstFit: A Tetris-style placement that finds the first available empty slot from the top-left (0,0) to recycle grid fragmentation.strategy parameter in both addItem and addItems on DashboardController.Custom Drag Handles & Gestures: Touch gestures on mobile devices were hardcoded to long-presses. Introduced the DragStartGesture configuration parameter (longPress, tap, none), alongside the DashboardDragStartListener and DashboardDelayedDragStartListener widgets. You can now fully customize gesture delays or designate dedicated drag handle icons to manipulate dashboard items.
Flicker-free Breakpoint Transitions: Resolved the single-frame flash/blink when resizing across viewport breakpoints. The grid now renders with the previous column layout on the transitioning frame before snapping to the new size, preserving scroll extents and preventing layout shifts. (thx @kamil-matula)
Fixed Horizontal Resizing Collisions: Resolved an issue where resizing items on horizontally scrollable dashboards resulted in vertical collision stacking. Collision pushes and secondary overlap resolutions now dynamically follow the controller's active compactionType instead of being vertically hardcoded.
Immoveable Static Items in Multi-Selection: Fixed a case where static items could be dragged and relocated if they were added to a multi-selection group.
Mobile Gesture Interruptions: Resolved an issue on touch devices where system alerts, dragging off-screen, or gesture takeovers left the dashboard locked in an active dragging state.
Asynchronous Scroll Safety: Fixed a bug in scrollToItem where an interrupted scroll animation or a disposed ScrollController would cause the returned Future to hang indefinitely. Scroll animations are now safely encapsulated in a try-catch block to correctly propagate exceptions.
Skyline Compactors Enabled: Upgraded the default dashboard compactors to use the optimized algorithms. This reduces the computational complexity of layout reflows, significantly improving frame rates during rapid dragging and window resizing on large layouts.
Non-Destructive Bounds Correction: Fixed a case where any item with a negative abscisse (x < 0) was resized to span the entire column count. The engine now safely clamps the item's width to the maximum available slot space, preserving its original user-defined size.
Negative Coordinates Sanitization: Fixed an edge case where importing malformed layouts with negative Y coordinates (like y: -1) under CompactType.none (NoCompactor) bypassed validation. It now cleanly clamps negative Y coordinates to 0.
Lints: Maintain backward compatibility with older Flutter versions by temporarily keeping CustomScrollView's cacheExtent. This deprecation is ignored locally for now and will be removed once Flutter version will stop to support it.
No breaking change in this release.
Programmatic Scrolling: Added scrollToItem to DashboardController.
Programmatic Scrolling: Added scrollToItem to DashboardController.
alignment (0.0 to 1.0), duration, and curve.Future<void> that completes only when the scroll animation is finished.jumpTo instead of animateTo when duration is set to Duration.zero.Fix: Invalid pointer location in items. (thx @hpoul)
Update state_beacon
Tests
Added Pluggable Compaction Strategies.
Added Pluggable Compaction Strategies.
CompactorDelegate and passing it to controller.setCompactor().Refactor: The internal compaction logic now uses the Strategy Pattern (VerticalCompactor, HorizontalCompactor).
Huge performance improvements on compaction algorithm
Added benchmark.dart in tests and results to readme
Added tests
Updated README and example
Fix: In free positioning mode (compaction: none), autoscroll of external draggable item.
Multi-Selection & Cluster Drag:
Shift + Click (or Ctrl/Meta) or customizable keys.multiSelectKeys to DashboardShortcuts to define which keys (e.g. Alt, Shift) trigger multi-selection.toggleSelection(id) or clearSelection().removeItems(List<String>) to the controller.onLayoutChanged(items) becomes onLayoutChanged(items, slotCount). This allows to persist layouts specifically for the current breakpoint.onItemDeleted(item) is replaced by onItemsDeleted(List<LayoutItem> items).onWillDelete(item) now receives List<LayoutItem> items.Feat: Enhanced Responsive Behavior with Layout Memory.
Fix: Improved Mini-Map rendering in horizontal mode to prevent the widget from becoming too small.
mainAxisSpacing and crossAxisSpacing.Feat: Added DashboardMinimap widget.
DashboardMinimap widget.
MinimapStyle.Feat: Added optimizeLayout() to the controller. This feature compacts the grid by filling empty gaps (defragmentation), respecting static items and th
optimizeLayout() to the controller. This feature compacts the grid by filling empty gaps (defragmentation), respecting static items and the visual order of elements.Added full Accessibility (A11y) support.
New feature:
Added full Accessibility (A11y) support.
Tab, grab items with Space/Enter, move them with Arrow Keys, and drop them with Space/Enter.SemanticsService to announce item selection, movement coordinates, and actions.DashboardGuidance.DashboardShortcuts (e.g. supporting WASD).Fix:
Fix: Fixed an issue where item, in sliver composition mode, was not correctly clipped when resizing beyond the viewport bounds.
- Improve pub score.
- Updated README.md
README.mdIntroduces Sliver direct composition via DashboardOverlay, and decouples interaction logic from the rendering layer.
New feature:
Introduces Sliver direct composition via DashboardOverlay, and decouples interaction logic from the rendering layer.
DashboardOverlay widget to handle all global interactions (drag, resize, trash, auto-scroll) and background rendering.SliverDashboard widget for direct composition within CustomScrollView (allows SliverAppBar, SliverList, etc.).Dashboard widget to use DashboardOverlay internally (backward compatibility).SliverDashboard to compose with others slivers, the grid stops precisely at the content end (allowing subsequent slivers to be visible).
If no subsequent slivers to be visible (eg. SliverAppBar + SliverDashboard), you can set fillViewport to true to extend grid in viewport.Dashboard widget, in an Expanded, the grid fills the viewport, and fillViewport has no action.Documentation:
README.md and ARCHITECTURE.md to reflect the new Overlay/Sliver architecture and document the fillViewport parameter.main_sliver.dart example demonstrating sliver composition.Refactored Controller API: DashboardController is now a strict interface. Internal methods (like onDragUpdate, dragOffset, etc.) are no longer exposed
Breaking Changes:
DashboardController is now a strict interface. Internal methods (like onDragUpdate, dragOffset, etc.) are no longer exposed publicly. This improves IDE autocompletion and prevents accidental misuse of internal logic.
DashboardControllerImpl (not recommended).Improvements:
DashboardControllerImpl to handle logic separately from the interface.Fix secondary collisions where multiple items pushed by the resizing element would temporarily overlap at the same Y coordinate.
Fix resize behavior during auto-scroll
* Updated README.md
Updated README.md contributing section, ARCHITECTURE.md and added an AGENTS.md file.
* Initial release.
* Initial release.
Your coding agent can read these notes before it upgrades. Set up the MCP server →