Grid layout package

My usecase is a grid for a timetable where the vertical coordinates are 15 min timeslots and the horizontal axis are events (in my case lessons) happening simultaneously. I can imagine this being a use case for others, too. Unfortunately I don’t have the time now to look at it myself, so just an idea for now. Thank you for sharing with the community!

oki. what you need is a time(table) widget. This is a bit different, but that might be an interesting challenge once I’ll have reached v1 (I hope soon).

Atm, target is dashboard/grid engine for stuff like analytics, iot (like grafana) etc. But sure, it can also be used for building UI with lazy loading (and it’s also composable with others slivers which is handy).

Actually, my goal is to fill the gap between Flutter and JS grid engines.. the direct β€œJS contender” is react-grid-layout (I’ve been heavily inspired by this package). And I added some fancy features and opti.

Remaining tasks on my todo:

  • Allow to create your own custom compaction and positioning algo
  • Sections/visual headers to segment the dashboard, with sticky option
  • Allow items to be dropped into or dragged out of groups.
1 Like

For those following my iterations, in next coming version (0.9) :

  • new feat: add your custom compaction algo, by implementing CompactorDelegate to define custom rules (e.g., specific gravity, fixed zones).
  • huge performance boost on compaction algo. noticeable when there are a lot of items in dashboard

Compaction algo comparison v.0.8 (old) vs 0.9 (new), on desktop:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          BENCHMARK RESULTS                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Test                                                 β”‚ Time              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ COMPACTION                                           β”‚                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Vertical Old (100 items)                           β”‚           1.22 ms β”‚
β”‚   Vertical New (100 items)                           β”‚             17 Β΅s β”‚
β”‚   Horizontal Old (100 items)                         β”‚            355 Β΅s β”‚
β”‚   Horizontal New (100 items)                         β”‚             17 Β΅s β”‚
β”‚   Vertical Old (500 items)                           β”‚          99.90 ms β”‚
β”‚   Vertical New (500 items)                           β”‚             99 Β΅s β”‚
β”‚   Horizontal Old (500 items)                         β”‚          10.30 ms β”‚
β”‚   Horizontal New (500 items)                         β”‚            101 Β΅s β”‚
β”‚   Vertical Old (1000 items)                          β”‚         898.88 ms β”‚
β”‚   Vertical New (1000 items)                          β”‚            222 Β΅s β”‚
β”‚   Horizontal Old (1000 items)                        β”‚          37.99 ms β”‚
β”‚   Horizontal New (1000 items)                        β”‚            217 Β΅s β”‚
β”‚ MOVE                                                 β”‚                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Move Element (100 items)                           β”‚             72 Β΅s β”‚
β”‚   Move Element (500 items)                           β”‚           1.40 ms β”‚
β”‚ SORT                                                 β”‚                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Sort Layout (100 items)                            β”‚              6 Β΅s β”‚
β”‚   Sort Layout (500 items)                            β”‚             43 Β΅s β”‚
β”‚   Sort Layout (1000 items)                           β”‚             99 Β΅s β”‚
β”‚ OPTIMIZE                                             β”‚                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Defrag (100 items)                                 β”‚           4.76 ms β”‚
β”‚   Defrag (500 items)                                 β”‚         855.42 ms β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Optimize Defrag is not called by dashboard, this is an api that can ponctually be used when you want to β€œdefragment” your dashboard.

Would be interesting to compare to the JS contender I mentioned previously. Although they use mutable state if I’m not wrong, and my package uses immutable and copyWith, I wouldn’t be suprised if we get better results thx to AOT (maybe same for web wasm..). When I’ll have more time, I’ll compare both.

Final stretch before the 1.0 …

2 Likes

POC: nested dashboards/groups

test590

  • Sections
  • Folders/Groups (nested dashboard)
  • scrollToItem by its logical id

test594

Hi there.

Since last time, sliver_dashboard has grown a lot, and the upcoming 2.0 feels like the right moment to check back in. It’s also getting more popularity on pub dev which I think is pretty cool and encouraging :slight_smile:

If there’s one sentence to remember, it’s this: sliver_dashboard is not a classic grid widget. It’s a deterministic, testable layout engine that happens to render through Flutter slivers.

You can use it to build interactive, draggable/resizable dashboard grid, on Flutter’s sliver protocol, so it virtualizes. Only visible tiles are built, and drag/resize stay allocation-free in the layout hot path. If you’ve used gridstack.js or react-grid-layout on the web, this is it; this is a native Flutter take on the same idea, with a single codebase, crossplatform.
If you read me.. Thank you Dart/Flutter Team :heart:

Smooth on Flutter Web/JS even when when I stress tested it with 1000 items, scroll, drag/drop feel smooth. You can already check this using v1.x demo link on pub dev

V2 is not released yet on pub dev, I need a few more days of testing, just to be sure. But it’s already available on github if you’re curious or need it.

This 1.x demo is available on pub dev:

single_grid

And, what’s new in upcoming 2.0:

  • Nested grids: embed a full dashboard inside any tile
  • Cross-grid drag & drop: move tiles between nested, sibling, or independent grids, with dimension projection between different column counts
  • Paint-phase reflow animations (opt-in): pushed tiles slide on the GPU, no widget rebuilds
  • A richer minimap (markers, multiple viewports)
  • etc.

nested_grids

multi_sliver_cross_drag

Hello.

As promised, sliver_dashboard v2 is now available on pub.dev! :rocket:

Built for β€œproduction-grade” stability:

  • 98% code coverage with 450 unit & widget tests to prevent layout regressions (8K lines of production code, 10K for tests).
  • Strict type safety and linting using very_good_analysis.

Check out the updated documentation:
[Link to pub.dev]

I might be biased, but I think this is a unique solution in the Flutter ecosystem; a true sliver-based alternative to GridStack and react-grid-layout.

I would love to hear your feedback, thoughts, or see what you build with it.

Enjoy!

I’m continuing to stress-test sliver_dashboard. This is purely out of curiosity, to see how far it’s possible to push the layout engine to its limits.. because to be honest, I’ve never seen a dashboard with 4 000 or 10 000 items :sweat_smile:

──────────────────────────────────────────────────────────────────────────────┐
β”‚ BENCHMARK RESULTS β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Test β”‚ Median β”‚ Best β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ COMPACTION (DEEP INPUT) β”‚ β”‚ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Vertical Standard (100 items) β”‚ 44 Β΅s β”‚ 42 Β΅s β”‚
β”‚ Vertical Fast/Tide (100 items) β”‚ 35 Β΅s β”‚ 34 Β΅s β”‚
β”‚ Horizontal Standard (100 items) β”‚ 89 Β΅s β”‚ 86 Β΅s β”‚
β”‚ Horizontal Fast/Tide (100 items) β”‚ 35 Β΅s β”‚ 34 Β΅s β”‚
β”‚ Vertical Standard (500 items) β”‚ 299 Β΅s β”‚ 293 Β΅s β”‚
β”‚ Vertical Fast/Tide (500 items) β”‚ 250 Β΅s β”‚ 238 Β΅s β”‚
β”‚ Horizontal Standard (500 items) β”‚ 487 Β΅s β”‚ 476 Β΅s β”‚
β”‚ Horizontal Fast/Tide (500 items) β”‚ 266 Β΅s β”‚ 262 Β΅s β”‚
β”‚ Vertical Standard (1000 items) β”‚ 586 Β΅s β”‚ 542 Β΅s β”‚
β”‚ Vertical Fast/Tide (1000 items) β”‚ 525 Β΅s β”‚ 509 Β΅s β”‚
β”‚ Horizontal Standard (1000 items) β”‚ 992 Β΅s β”‚ 900 Β΅s β”‚
β”‚ Horizontal Fast/Tide (1000 items) β”‚ 568 Β΅s β”‚ 539 Β΅s β”‚
β”‚ Vertical Standard (2000 items) β”‚ 1.35 ms β”‚ 1.29 ms β”‚
β”‚ Vertical Fast/Tide (2000 items) β”‚ 1.15 ms β”‚ 1.12 ms β”‚
β”‚ Horizontal Standard (2000 items) β”‚ 2.14 ms β”‚ 2.04 ms β”‚
β”‚ Horizontal Fast/Tide (2000 items) β”‚ 1.27 ms β”‚ 1.21 ms β”‚
β”‚ Vertical Standard (4000 items) β”‚ 2.88 ms β”‚ 2.51 ms β”‚
β”‚ Vertical Fast/Tide (4000 items) β”‚ 2.62 ms β”‚ 2.54 ms β”‚
β”‚ Horizontal Standard (4000 items) β”‚ 4.69 ms β”‚ 4.54 ms β”‚
β”‚ Horizontal Fast/Tide (4000 items) β”‚ 2.70 ms β”‚ 2.53 ms β”‚
β”‚ Vertical Standard (10000 items) β”‚ 7.49 ms β”‚ 7.30 ms β”‚
β”‚ Vertical Fast/Tide (10000 items) β”‚ 7.66 ms β”‚ 7.44 ms β”‚
β”‚ Horizontal Standard (10000 items) β”‚ 20.31 ms β”‚ 18.85 ms β”‚
β”‚ Horizontal Fast/Tide (10000 items) β”‚ 7.58 ms β”‚ 7.07 ms β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ COMPACTION (ALREADY COMPACT) β”‚ β”‚ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Vertical Standard (100 items) β”‚ 35 Β΅s β”‚ 34 Β΅s β”‚
β”‚ Vertical Standard (500 items) β”‚ 203 Β΅s β”‚ 200 Β΅s β”‚
β”‚ Vertical Standard (1000 items) β”‚ 445 Β΅s β”‚ 441 Β΅s β”‚
β”‚ Vertical Standard (2000 items) β”‚ 1.06 ms β”‚ 961 Β΅s β”‚
β”‚ Vertical Standard (4000 items) β”‚ 2.15 ms β”‚ 2.13 ms β”‚
β”‚ Vertical Standard (10000 items) β”‚ 6.69 ms β”‚ 6.14 ms β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ RESIZE (TOP OF GRID) β”‚ β”‚ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ resizeItem push at top (500 items) β”‚ 1.10 ms β”‚ 1.07 ms β”‚
β”‚ resizeItem push at top (1000 items) β”‚ 816 Β΅s β”‚ 766 Β΅s β”‚
β”‚ resizeItem push at top (2000 items) β”‚ 139 Β΅s β”‚ 135 Β΅s β”‚
β”‚ resizeItem push at top (4000 items) β”‚ 292 Β΅s β”‚ 288 Β΅s β”‚
β”‚ resizeItem push at top (10000 items) β”‚ 27.66 ms β”‚ 25.86 ms β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ MOVE β”‚ β”‚ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Move Element (100 items) β”‚ 61 Β΅s β”‚ 57 Β΅s β”‚
β”‚ Move Element (500 items) β”‚ 389 Β΅s β”‚ 348 Β΅s β”‚
β”‚ Move Element (1000 items) β”‚ 705 Β΅s β”‚ 699 Β΅s β”‚
β”‚ Move Element (2000 items) β”‚ 1.48 ms β”‚ 1.46 ms β”‚
β”‚ Move Element (4000 items) β”‚ 3.18 ms β”‚ 2.91 ms β”‚
β”‚ Move Element (10000 items) β”‚ 8.77 ms β”‚ 8.22 ms β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ SORT β”‚ β”‚ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Sort Layout (100 items) β”‚ 14 Β΅s β”‚ 14 Β΅s β”‚
β”‚ Sort Layout (500 items) β”‚ 93 Β΅s β”‚ 91 Β΅s β”‚
β”‚ Sort Layout (1000 items) β”‚ 213 Β΅s β”‚ 204 Β΅s β”‚
β”‚ Sort Layout (2000 items) β”‚ 512 Β΅s β”‚ 472 Β΅s β”‚
β”‚ Sort Layout (4000 items) β”‚ 1.03 ms β”‚ 1.01 ms β”‚
β”‚ Sort Layout (10000 items) β”‚ 4.02 ms β”‚ 3.83 ms β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ OPTIMIZE β”‚ β”‚ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Defrag (100 items) β”‚ 236 Β΅s β”‚ 234 Β΅s β”‚
β”‚ Defrag (500 items) β”‚ 1.77 ms β”‚ 1.62 ms β”‚
β”‚ Defrag (1000 items) β”‚ 3.63 ms β”‚ 3.56 ms β”‚
β”‚ Defrag (2000 items) β”‚ 8.58 ms β”‚ 7.88 ms β”‚
β”‚ Defrag (4000 items) β”‚ 21.87 ms β”‚ 21.05 ms β”‚
β”‚ Defrag (10000 items) β”‚ 92.49 ms β”‚ 79.24 ms β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Note: operations like Defrag are separate from realtime interaction loops. While draggingn, resizing and scrolling run many times/sec on the layout β€œhot path”, which is why their sub 10ms perf is so critical, defragmentation on the other side is an on-demand operation usually triggered by a user clicking a button and has no impact on UI fluidity.
I benchmark sliver_dashboard engine in AOT mode, on an β€œold” Ryzen 2600 machine, Windows 11.

Release 2.2.0.

Brings a few optimizations, and 100% code coverage.

I think it’s feature complete, at least for me, but if you need a feature or any help, don’t hesitate to ask me.
I use it in production projects, so I’ll continue maintenance.

Sadly I tried to showcase it on reddit flutterdev, my handwritten posts got filtered twice.. no idea why, maybe because I’m a new user, no way to contact modos, so, never mind, I’m giving up to showcase it there.

Have a good day!

I just released sliver_dashboard 2.4.0.

It allows dropping into collapsible folders/hosts, and also provides a new projection policy preservePixelSizeβ€œ.

I modified the nested example to show how you can now build size-driven collapsible folders by using the itemBreakpointBuilder (instead of the regular itemBuilder), which rebuilds only when its item breakpoint resolver callback is called:
shrink a card to collapse it into an interactive drop-target icon (onItemDroppedOnHost) to file items away, or expand it to reopen the full nested grid.

And you can pair it in your projects, with the new preservePixelSize projection policy, dragged tiles will automatically adapt across different grid densities while preserving their exact physical screen size.

drag drop

Here’s how sliver_dashboard compares to two well-known Web grid libraries.

Feature sliver_dashboard GridStack React Grid Layout
Ecosystem Dart/Flutter JS/TypeScript JS/TypeScript
Platforms :white_check_mark: Android, iOS, Windows, macOS, Linux, Web (JS & Wasm) :globe_with_meridians: Web :globe_with_meridians: Web
Vertical dashboard :white_check_mark: :white_check_mark: :white_check_mark:
Horizontal dashboard :white_check_mark: :cross_mark: :warning: Horizontal compaction only
Segmented grids / Section barriers :white_check_mark: :cross_mark: :cross_mark:
Native scroll integration :white_check_mark: Slivers HTML scrolling HTML scrolling
Viewport virtualization :white_check_mark: Native :cross_mark: (lazy loading only) :cross_mark:
Drag & Drop :white_check_mark: :white_check_mark: :white_check_mark:
Custom drag handles :white_check_mark: :white_check_mark: :white_check_mark:
Resize :white_check_mark: :white_check_mark: :white_check_mark:
Responsive layouts :white_check_mark: :white_check_mark: :white_check_mark:
Per-item breakpoints :white_check_mark: :cross_mark: :cross_mark:
Nested dashboards :white_check_mark: :white_check_mark: :warning: Custom implementation
Collapsible nested dashboards :white_check_mark: :cross_mark: :cross_mark:
Drag between dashboards :white_check_mark: :white_check_mark: :warning: Custom implementation
Cross-sliver / Multi-sliver drag :white_check_mark: :cross_mark: :cross_mark:
External drag sources :white_check_mark: :white_check_mark: :warning:
Built-in trash / delete target :white_check_mark: :warning: Option β€˜removable’ :cross_mark:
Built-in guidance / drop hints :white_check_mark: :cross_mark: :cross_mark:
Push collision :white_check_mark: :white_check_mark: :white_check_mark:
Push-or-shrink collision :white_check_mark: :cross_mark: :cross_mark:
Auto-shrink neighbors on drag :white_check_mark: :cross_mark: :cross_mark:
Reflow animations :white_check_mark: :white_check_mark: :warning: CSS-dependent
Layout persistence :white_check_mark: :white_check_mark: :white_check_mark:
Layout import / export :white_check_mark: :white_check_mark: :white_check_mark:
Extensible layout algorithms :white_check_mark: :cross_mark: :white_check_mark:
Extensible compaction algorithms :white_check_mark: :warning: :white_check_mark:
Multi-selection :white_check_mark: :cross_mark: :cross_mark:
Mini-map :white_check_mark: :cross_mark: :cross_mark:
Keyboard accessibility :white_check_mark: :warning: Limited :warning: Limited
Screen reader announcement :white_check_mark: :cross_mark: :cross_mark:
Large-scale dashboards (1000+ items) :white_check_mark: ️ Not documented ️ Not documented

But why does it matter?

β€œShow me your architecture, not your features.”

If you’re curious about how and why sliver_dashboard was designed this way, here are a few links:

Note: I used AI assistants during development (Gemini, Claude), but with a strict discipline to ensure long-term maintainability and robustness: no coding agents (no auto-commit, no β€œlet it run free”). I wanted for this project to stay in full control and truly know my codebase; not delegate ownership to an agent.
Only chat-based assistance, with careful review, all decisions human-driven.
I learnt so many things during this process, about custom slivers etc, I don’t regret it even if it has been more work than using a coding agent (using only chat also saved some credits on my AI subscriptions for my others projects. less requests, tools calls..).

Feedback, questions, and ideas welcome.

sliver_dashboard v2.5 is available.

  • You can revert or re-apply layout changes with controller.undo() / redo(), reactive canUndo / canRedo, and async veto hooks. It can be disabled with maxHistoryLength: 0. Default is 30.
  • Tiles displaced by push cascades can be highlighted during drag operations (β€œImpact Preview”). This configurable visual feedback is also painted live on the DashboardMinimap.
  • Hold Alt or Option (configurable key) when dragging a tile to pull out a copy while leaving the original in place. Driven by your application’s onCloneRequested callback to mint new IDs and custom metadata.

Impact preview, and undo/redo:
impact preview