From 843add4016fd41c52745efdd61c89523576f4ded Mon Sep 17 00:00:00 2001 From: tal Date: Wed, 25 Mar 2026 14:50:09 +0200 Subject: [PATCH 1/4] Added the capability to initialize `isEditing` from the ctor if desired --- lib/src/controller/dashboard_controller.dart | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/src/controller/dashboard_controller.dart b/lib/src/controller/dashboard_controller.dart index 2a67ad5..95f74cd 100644 --- a/lib/src/controller/dashboard_controller.dart +++ b/lib/src/controller/dashboard_controller.dart @@ -23,9 +23,11 @@ class DashboardItemController with ChangeNotifier { /// Changes cannot be handled. DashboardItemController({ required List items, + bool isEditing = false, }) : _items = items.asMap().map( (key, value) => MapEntry(value.identifier, value), ), + _initialIsEditing = isEditing, itemStorageDelegate = null; /// You can create [DashboardItemController] with an [itemStorageDelegate]. @@ -34,15 +36,21 @@ class DashboardItemController with ChangeNotifier { /// /// If the delegate is waiting for a Future to load the items, this will throw /// error at the end of the [timout]. - DashboardItemController.withDelegate( - {Duration? timeout, required this.itemStorageDelegate}) - : _timeout = timeout ?? const Duration(seconds: 10); + DashboardItemController.withDelegate({ + Duration? timeout, + required this.itemStorageDelegate, + bool isEditing = false, + }) : _timeout = timeout ?? const Duration(seconds: 10), + _initialIsEditing = isEditing; /// To define [itemStorageDelegate] use [DashboardItemController.withDelegate] /// /// For more see [DashboardItemStorageDelegate] documentation. final DashboardItemStorageDelegate? itemStorageDelegate; + /// Initial editing state for the controller. + final bool _initialIsEditing; + /// Users can only edit the layout when [isEditing] is true. /// The [isEditing] does not have to be true to add or delete items. /// @@ -206,6 +214,7 @@ class DashboardItemController with ChangeNotifier { void _attach(_DashboardLayoutController layoutController) { _layoutController = layoutController; + _layoutController!.isEditing = _initialIsEditing; } } From 399d2796b6cdf39652482dd0f032498f70a7c5e8 Mon Sep 17 00:00:00 2001 From: tal Date: Wed, 25 Mar 2026 19:41:25 +0200 Subject: [PATCH 2/4] added `child` widget support as the "background" presented element --- lib/src/widgets/dashboard.dart | 9 +++++++-- lib/src/widgets/dashboard_stack.dart | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/src/widgets/dashboard.dart b/lib/src/widgets/dashboard.dart index ee3c246..6e2b929 100644 --- a/lib/src/widgets/dashboard.dart +++ b/lib/src/widgets/dashboard.dart @@ -49,7 +49,8 @@ class Dashboard extends StatefulWidget { this.absorbPointer = true, this.animateEverytime = true, this.itemStyle = const ItemStyle(), - this.scrollToAdded = true}) + this.scrollToAdded = true, + this.child}) : assert((slotHeight == null && slotAspectRatio == null) || !(slotHeight != null && slotAspectRatio != null)), editModeSettings = editModeSettings ?? EditModeSettings(), @@ -195,6 +196,9 @@ class Dashboard extends StatefulWidget { /// Look [Material] documentation for more. final ItemStyle itemStyle; + /// Optional child widget to be added to the Stack. + final Widget? child; + @override State> createState() => _DashboardState(); } @@ -454,7 +458,8 @@ class _DashboardState extends State> key: _stateKey, itemBuilder: widget.itemBuilder, dashboardController: _layoutController, - offset: offset); + offset: offset, + child: widget.child); }); } } diff --git a/lib/src/widgets/dashboard_stack.dart b/lib/src/widgets/dashboard_stack.dart index fe8098c..204621f 100644 --- a/lib/src/widgets/dashboard_stack.dart +++ b/lib/src/widgets/dashboard_stack.dart @@ -12,7 +12,8 @@ class _DashboardStack extends StatefulWidget { required this.onScrollStateChange, required this.shouldCalculateNewDimensions, required this.itemStyle, - required this.emptyPlaceholder}) + required this.emptyPlaceholder, + this.child}) : super(key: key); final Widget? emptyPlaceholder; @@ -30,6 +31,8 @@ class _DashboardStack extends StatefulWidget { final void Function() shouldCalculateNewDimensions; + final Widget? child; + @override State<_DashboardStack> createState() => _DashboardStackState(); } @@ -223,6 +226,7 @@ class _DashboardStackState Widget result = Stack( clipBehavior: Clip.hardEdge, children: [ + if (widget.child != null) widget.child!, if (widget.editModeSettings.paintBackgroundLines && widget.dashboardController.isEditing) Positioned( @@ -259,7 +263,7 @@ class _DashboardStackState : [ buildPositioned(_widgetsMap[ widget.dashboardController.editSession?.editing.id]!) - ]) + ]), ], ); From 0dd88820bc8400d458cf0d567cce7e3955c23a54 Mon Sep 17 00:00:00 2001 From: tal Date: Wed, 25 Mar 2026 20:23:04 +0200 Subject: [PATCH 3/4] alignment --- lib/src/widgets/dashboard.dart | 46 +++++++++++----------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/lib/src/widgets/dashboard.dart b/lib/src/widgets/dashboard.dart index 6e2b929..09d1d23 100644 --- a/lib/src/widgets/dashboard.dart +++ b/lib/src/widgets/dashboard.dart @@ -51,8 +51,7 @@ class Dashboard extends StatefulWidget { this.itemStyle = const ItemStyle(), this.scrollToAdded = true, this.child}) - : assert((slotHeight == null && slotAspectRatio == null) || - !(slotHeight != null && slotAspectRatio != null)), + : assert((slotHeight == null && slotAspectRatio == null) || !(slotHeight != null && slotAspectRatio != null)), editModeSettings = editModeSettings ?? EditModeSettings(), super(key: key); @@ -203,8 +202,7 @@ class Dashboard extends StatefulWidget { State> createState() => _DashboardState(); } -class _DashboardState extends State> - with TickerProviderStateMixin { +class _DashboardState extends State> with TickerProviderStateMixin { /// @override void initState() { @@ -233,8 +231,7 @@ class _DashboardState extends State> AsyncSnapshot? get _snap => widget.dashboardItemController._asyncSnap?.value; - bool get _withDelegate => - widget.dashboardItemController.itemStorageDelegate != null; + bool get _withDelegate => widget.dashboardItemController.itemStorageDelegate != null; /// late _DashboardLayoutController _layoutController; @@ -304,21 +301,16 @@ class _DashboardState extends State> if (widget.slotHeight != null) { h = widget.slotHeight!; } else if (widget.slotAspectRatio != null) { - h = _layoutController._viewportDelegate.resolvedConstrains.maxWidth / - widget.slotCount / - widget.slotAspectRatio!; + h = _layoutController._viewportDelegate.resolvedConstrains.maxWidth / widget.slotCount / widget.slotAspectRatio!; } else { - h = _layoutController._viewportDelegate.resolvedConstrains.maxWidth / - widget.slotCount; + h = _layoutController._viewportDelegate.resolvedConstrains.maxWidth / widget.slotCount; } - _layoutController._setSizes( - _layoutController._viewportDelegate.resolvedConstrains, h); + _layoutController._setSizes(_layoutController._viewportDelegate.resolvedConstrains, h); _offset = o; - offset.applyViewportDimension( - _layoutController._viewportDelegate.constraints.maxHeight); + offset.applyViewportDimension(_layoutController._viewportDelegate.constraints.maxHeight); var maxIndex = (_layoutController._endsTree.lastKey() ?? 0); @@ -341,11 +333,9 @@ class _DashboardState extends State> late double _maxExtend; /// - final GlobalKey<_DashboardStackState> _stateKey = - GlobalKey<_DashboardStackState>(); + final GlobalKey<_DashboardStackState> _stateKey = GlobalKey<_DashboardStackState>(); - final GlobalKey _scrollableKey = - GlobalKey(); + final GlobalKey _scrollableKey = GlobalKey(); bool scrollable = true; @@ -359,8 +349,7 @@ class _DashboardState extends State> (!_reloading || differentReload) && _layoutController.slotCount != widget.slotCount && _withDelegate && - widget - .dashboardItemController.itemStorageDelegate!.layoutsBySlotCount) { + widget.dashboardItemController.itemStorageDelegate!.layoutsBySlotCount) { _reloading = true; _reloadFor = widget.slotCount; widget.dashboardItemController._items.clear(); @@ -407,11 +396,8 @@ class _DashboardState extends State> if (_withDelegate) { if (_snap!.connectionState == ConnectionState.none) { _building = false; - return widget.errorPlaceholder - ?.call(_snap!.error!, _snap!.stackTrace!) ?? - const SizedBox(); - } else if (_snap!.connectionState == ConnectionState.waiting || - _reloading) { + return widget.errorPlaceholder?.call(_snap!.error!, _snap!.stackTrace!) ?? const SizedBox(); + } else if (_snap!.connectionState == ConnectionState.waiting || _reloading) { _building = false; return widget.loadingPlaceholder ?? @@ -427,8 +413,7 @@ class _DashboardState extends State> Widget dashboardWidget(BoxConstraints constrains) { return Scrollable( - physics: - scrollable ? widget.physics : const NeverScrollableScrollPhysics(), + physics: scrollable ? widget.physics : const NeverScrollableScrollPhysics(), key: _scrollableKey, controller: widget.scrollController, semanticChildCount: widget.dashboardItemController._items.length, @@ -465,10 +450,7 @@ class _DashboardState extends State> } class _ItemCurrentPositionTween extends Tween<_ItemCurrentPosition> { - _ItemCurrentPositionTween( - {required _ItemCurrentPosition begin, - required _ItemCurrentPosition end, - required this.onlyDimensions}) + _ItemCurrentPositionTween({required _ItemCurrentPosition begin, required _ItemCurrentPosition end, required this.onlyDimensions}) : super(begin: begin, end: end); bool onlyDimensions; From 88be06c4e5ca42d03d01292ee02d4bd73a45273a Mon Sep 17 00:00:00 2001 From: tal Date: Wed, 25 Mar 2026 20:24:44 +0200 Subject: [PATCH 4/4] alignment --- lib/src/widgets/dashboard_stack.dart | 109 +++++++-------------------- 1 file changed, 29 insertions(+), 80 deletions(-) diff --git a/lib/src/widgets/dashboard_stack.dart b/lib/src/widgets/dashboard_stack.dart index 204621f..db05151 100644 --- a/lib/src/widgets/dashboard_stack.dart +++ b/lib/src/widgets/dashboard_stack.dart @@ -37,13 +37,11 @@ class _DashboardStack extends StatefulWidget { State<_DashboardStack> createState() => _DashboardStackState(); } -class _DashboardStackState - extends State<_DashboardStack> { +class _DashboardStackState extends State<_DashboardStack> { /// ViewportOffset get viewportOffset => widget.offset; - _ViewportDelegate get viewportDelegate => - widget.dashboardController._viewportDelegate; + _ViewportDelegate get viewportDelegate => widget.dashboardController._viewportDelegate; /// double get pixels => viewportOffset.pixels; @@ -94,10 +92,8 @@ class _DashboardStackState return _DashboardItemWidget( style: widget.itemStyle, key: _keys[list[2]]!, - itemGlobalPosition: (list[0] as _ItemCurrentLayout)._currentPosition( - viewportDelegate: viewportDelegate, - slotEdge: slotEdge, - verticalSlotEdge: verticalSlotEdge), + itemGlobalPosition: + (list[0] as _ItemCurrentLayout)._currentPosition(viewportDelegate: viewportDelegate, slotEdge: slotEdge, verticalSlotEdge: verticalSlotEdge), itemCurrentLayout: list[0], id: list[2], editModeSettings: widget.editModeSettings, @@ -126,8 +122,7 @@ class _DashboardStackState shape: widget.itemStyle.shape, color: widget.itemStyle.color, clipBehavior: widget.itemStyle.clipBehavior ?? Clip.none, - animationDuration: - widget.itemStyle.animationDuration ?? kThemeChangeDuration, + animationDuration: widget.itemStyle.animationDuration ?? kThemeChangeDuration, child: widget.itemBuilder(i), //shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), )), @@ -155,8 +150,7 @@ class _DashboardStackState var endPixels = viewportOffset.pixels + height + widget.cacheExtend; var endY = (endPixels / verticalSlotEdge).ceil(); - var endIndex = widget.dashboardController - .getIndex([widget.dashboardController.slotCount - 1, endY]); + var endIndex = widget.dashboardController.getIndex([widget.dashboardController.slotCount - 1, endY]); var needs = []; var key = startIndex; @@ -227,43 +221,23 @@ class _DashboardStackState clipBehavior: Clip.hardEdge, children: [ if (widget.child != null) widget.child!, - if (widget.editModeSettings.paintBackgroundLines && - widget.dashboardController.isEditing) + if (widget.editModeSettings.paintBackgroundLines && widget.dashboardController.isEditing) Positioned( top: viewportDelegate.padding.top, left: viewportDelegate.padding.left, - width: viewportDelegate.constraints.maxWidth - - viewportDelegate.padding.vertical, - height: viewportDelegate.constraints.maxHeight - - viewportDelegate.padding.horizontal, + width: viewportDelegate.constraints.maxWidth - viewportDelegate.padding.vertical, + height: viewportDelegate.constraints.maxHeight - viewportDelegate.padding.horizontal, child: Builder(builder: (context) { return _AnimatedBackgroundPainter( - layoutController: widget.dashboardController, - editModeSettings: widget.editModeSettings, - offset: viewportOffset); + layoutController: widget.dashboardController, editModeSettings: widget.editModeSettings, offset: viewportOffset); }), ), - ..._widgetsMap.entries - .where((element) => - element.value[2] != - widget.dashboardController.editSession?.editing.id) - .map((e) { + ..._widgetsMap.entries.where((element) => element.value[2] != widget.dashboardController.editSession?.editing.id).map((e) { return buildPositioned(e.value); }).toList(), - if (widget.dashboardController.itemController._items.isEmpty && - !widget.dashboardController._isEditing) - Positioned( - bottom: 0, - left: 0, - right: 0, - top: 0, - child: widget.emptyPlaceholder ?? Container()), - ...?(widget.dashboardController.editSession == null - ? null - : [ - buildPositioned(_widgetsMap[ - widget.dashboardController.editSession?.editing.id]!) - ]), + if (widget.dashboardController.itemController._items.isEmpty && !widget.dashboardController._isEditing) + Positioned(bottom: 0, left: 0, right: 0, top: 0, child: widget.emptyPlaceholder ?? Container()), + ...?(widget.dashboardController.editSession == null ? null : [buildPositioned(_widgetsMap[widget.dashboardController.editSession?.editing.id]!)]), ], ); @@ -353,50 +327,37 @@ class _DashboardStackState Offset holdOffset = Offset.zero; void _onMoveStart(Offset local) { - var holdGlobal = Offset(local.dx - viewportDelegate.padding.left, - local.dy - viewportDelegate.padding.top); + var holdGlobal = Offset(local.dx - viewportDelegate.padding.left, local.dy - viewportDelegate.padding.top); var x = (local.dx - viewportDelegate.padding.left) ~/ slotEdge; - var y = - (local.dy + pixels - viewportDelegate.padding.top) ~/ verticalSlotEdge; + var y = (local.dy + pixels - viewportDelegate.padding.top) ~/ verticalSlotEdge; - var e = widget.dashboardController - ._indexesTree[widget.dashboardController.getIndex([x, y])]; + var e = widget.dashboardController._indexesTree[widget.dashboardController.getIndex([x, y])]; if (e is String) { var directions = []; _editing = widget.dashboardController._layouts![e]!; - var current = _editing!._currentPosition( - slotEdge: slotEdge, - viewportDelegate: viewportDelegate, - verticalSlotEdge: verticalSlotEdge); + var current = _editing!._currentPosition(slotEdge: slotEdge, viewportDelegate: viewportDelegate, verticalSlotEdge: verticalSlotEdge); var itemGlobal = _ItemCurrentPosition( - x: current.x - viewportDelegate.padding.left, - y: current.y - viewportDelegate.padding.top - pixels, - height: current.height, - width: current.width); + x: current.x - viewportDelegate.padding.left, y: current.y - viewportDelegate.padding.top - pixels, height: current.height, width: current.width); if (holdGlobal.dx < itemGlobal.x || holdGlobal.dy < itemGlobal.y) { _editing = null; setState(() {}); return; } - if (itemGlobal.x + widget.editModeSettings.resizeCursorSide > - holdGlobal.dx) { + if (itemGlobal.x + widget.editModeSettings.resizeCursorSide > holdGlobal.dx) { directions.add(AxisDirection.left); } - if ((itemGlobal.y) + widget.editModeSettings.resizeCursorSide > - holdGlobal.dy) { + if ((itemGlobal.y) + widget.editModeSettings.resizeCursorSide > holdGlobal.dy) { directions.add(AxisDirection.up); } - if (itemGlobal.endX - widget.editModeSettings.resizeCursorSide < - holdGlobal.dx) { + if (itemGlobal.endX - widget.editModeSettings.resizeCursorSide < holdGlobal.dx) { directions.add(AxisDirection.right); } - if ((itemGlobal.endY) - widget.editModeSettings.resizeCursorSide < - holdGlobal.dy) { + if ((itemGlobal.endY) - widget.editModeSettings.resizeCursorSide < holdGlobal.dy) { directions.add(AxisDirection.down); } if (directions.isNotEmpty) { @@ -411,10 +372,7 @@ class _DashboardStackState holdOffset = holdGlobal - Offset(itemGlobal.x, itemGlobal.y); var l = widget.dashboardController._layouts![e]; - widget.dashboardController.editSession!.editing._originSize = [ - l!.width, - l.height - ]; + widget.dashboardController.editSession!.editing._originSize = [l!.width, l.height]; setState(() {}); widget.onScrollStateChange(false); } else { @@ -435,8 +393,7 @@ class _DashboardStackState Offset? _moveStartOffset; double? _startScrollPixels; - bool isResizing(AxisDirection direction) => - _holdDirections!.contains(direction); + bool isResizing(AxisDirection direction) => _holdDirections!.contains(direction); void _onMoveUpdate(Offset local) { if (_editing != null) { @@ -456,8 +413,7 @@ class _DashboardStackState if (resizeMoveResult.isChanged) { setState(() { - _moveStartOffset = - _moveStartOffset! + resizeMoveResult.startDifference; + _moveStartOffset = _moveStartOffset! + resizeMoveResult.startDifference; _widgetsMap.remove(_editing!.id); for (var r in differences) { _widgetsMap.remove(r); @@ -468,14 +424,10 @@ class _DashboardStackState }); } } else { - var resizeMoveResult = _editing!._transformUpdate( - local - _moveStartOffset!, - pixels - _startScrollPixels!, - holdOffset); + var resizeMoveResult = _editing!._transformUpdate(local - _moveStartOffset!, pixels - _startScrollPixels!, holdOffset); if (resizeMoveResult != null && resizeMoveResult.isChanged) { setState(() { - _moveStartOffset = - _moveStartOffset! + resizeMoveResult.startDifference; + _moveStartOffset = _moveStartOffset! + resizeMoveResult.startDifference; _widgetsMap.remove(_editing!.id); if (_editing!._endIndex > (e)) { @@ -489,10 +441,7 @@ class _DashboardStackState void _onMoveEnd() { _editing?._key = _keys[_editing!.id]!; - _editing?._key.currentState - ?._setLast( - _editing!._transform?.value, _editing!._resizePosition?.value) - .then((value) { + _editing?._key.currentState?._setLast(_editing!._transform?.value, _editing!._resizePosition?.value).then((value) { widget.dashboardController.editSession?.editing._originSize = null; _editing?._clearListeners(); _editing = null;