From 9ea4c8b2605720d09cfb93c3c4bb3422a7243d8c Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 4 Aug 2022 18:19:53 +0800 Subject: [PATCH] chore: add overlap dragTarget interceptor --- .../flowy_board/lib/src/widgets/board.dart | 141 +++++++++++++++--- .../widgets/board_column/board_column.dart | 8 +- .../widgets/board_column/data_controller.dart | 7 +- .../lib/src/widgets/column_container.dart | 88 ----------- .../lib/src/widgets/flex/drag_state.dart | 18 ++- .../widgets/flex/drag_target_inteceptor.dart | 136 +++++++++++------ .../lib/src/widgets/flex/reorder_flex.dart | 35 +++-- .../widgets/phantom/phantom_controller.dart | 25 ++-- 8 files changed, 270 insertions(+), 188 deletions(-) delete mode 100644 frontend/app_flowy/packages/flowy_board/lib/src/widgets/column_container.dart diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board.dart index 5daffdb47b..2a49de26ba 100644 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board.dart +++ b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board.dart @@ -4,7 +4,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../flowy_board.dart'; -import 'column_container.dart'; +import '../rendering/board_overlay.dart'; +import 'flex/drag_target_inteceptor.dart'; import 'flex/reorder_flex.dart'; import 'phantom/phantom_controller.dart'; @@ -57,22 +58,24 @@ class Board extends StatelessWidget { Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: dataController, - child: Consumer( + child: Consumer( builder: (context, notifier, child) { - List children = []; - List acceptColumns = - dataController.columnControllers.keys.toList(); + List children = dataController.columnDatas.map((columnData) { + final controller = dataController.columnController(columnData.id); - dataController.columnControllers.forEach((columnId, controller) { - Widget child = _buildColumn(columnId, acceptColumns, controller); - children.add(child); - }); + return _buildColumn( + columnData.id, + dataController.columnIds, + controller, + ); + }).toList(); return BoardColumnContainer( - onReorder: (fromIndex, toIndex) {}, + onReorder: dataController.onReorder, boardDataController: dataController, background: background, spacing: spacing, + delegate: phantomController, children: children, ); }, @@ -97,7 +100,7 @@ class Board extends StatelessWidget { headerBuilder: headerBuilder, footBuilder: footBuilder, cardBuilder: cardBuilder, - acceptColumns: acceptColumns, + acceptedColumns: acceptColumns, dataController: dataController, scrollController: ScrollController(), onReorder: (_, int fromIndex, int toIndex) { @@ -114,33 +117,133 @@ class Board extends StatelessWidget { class BoardDataController extends ChangeNotifier with EquatableMixin, BoardPhantomControllerDelegate, ReoderFlextDataSource { - final LinkedHashMap columnDatas = LinkedHashMap(); - final LinkedHashMap columnControllers = + final List _columnDatas = []; + + List get columnDatas => _columnDatas; + + List get columnIds => + _columnDatas.map((columnData) => columnData.id).toList(); + + final LinkedHashMap _columnControllers = LinkedHashMap(); BoardDataController(); void setColumnData(BoardColumnData columnData) { final controller = BoardColumnDataController(columnData: columnData); - columnDatas[columnData.id] = columnData; - columnControllers[columnData.id] = controller; + _columnDatas.add(columnData); + _columnControllers[columnData.id] = controller; + } + + BoardColumnDataController columnController(String columnId) { + return _columnControllers[columnId]!; + } + + void onReorder(int fromIndex, int toIndex) { + final columnData = _columnDatas.removeAt(fromIndex); + _columnDatas.insert(toIndex, columnData); + notifyListeners(); } @override List get props { - return [columnDatas.values]; + return [_columnDatas]; } @override BoardColumnDataController? controller(String columnId) { - return columnControllers[columnId]; + return _columnControllers[columnId]; } @override String get identifier => '$BoardDataController'; @override - List get items => columnDatas.values.toList(); + List get items => _columnDatas; } -class BoardDataIdentifier {} +class BoardColumnContainer extends StatefulWidget { + final ScrollController? scrollController; + final OnDragStarted? onDragStarted; + final OnReorder onReorder; + final OnDragEnded? onDragEnded; + final BoardDataController boardDataController; + final List children; + final Widget? background; + final double spacing; + final ReorderFlexConfig config; + + final OverlapReorderFlexDragTargetDelegate delegate; + + const BoardColumnContainer({ + required this.onReorder, + required this.children, + required this.delegate, + required this.boardDataController, + this.onDragStarted, + this.onDragEnded, + this.scrollController, + this.background, + this.spacing = 0.0, + this.config = const ReorderFlexConfig(), + Key? key, + }) : super(key: key); + + @override + State createState() => _BoardColumnContainerState(); +} + +class _BoardColumnContainerState extends State { + final GlobalKey _columnContainerOverlayKey = + GlobalKey(debugLabel: '$BoardColumnContainer overlay key'); + late BoardOverlayEntry _overlayEntry; + + @override + void initState() { + _overlayEntry = BoardOverlayEntry( + builder: (BuildContext context) { + final interceptor = OverlapReorderFlexDragTargetInteceptor( + reorderFlexId: widget.boardDataController.identifier, + acceptedReorderFlexId: widget.boardDataController.columnIds, + delegate: widget.delegate, + ); + + Widget reorderFlex = ReorderFlex( + key: widget.key, + scrollController: widget.scrollController, + config: widget.config, + onDragStarted: widget.onDragStarted, + onReorder: widget.onReorder, + onDragEnded: widget.onDragEnded, + dataSource: widget.boardDataController, + direction: Axis.horizontal, + spacing: widget.spacing, + interceptor: interceptor, + children: widget.children, + ); + + return _wrapStack(reorderFlex); + }, + opaque: false, + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BoardOverlay( + key: _columnContainerOverlayKey, + initialEntries: [_overlayEntry], + ); + } + + Widget _wrapStack(Widget child) { + return Stack( + alignment: AlignmentDirectional.topStart, + children: [ + if (widget.background != null) widget.background!, + child, + ], + ); + } +} diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/board_column.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/board_column.dart index 92b4959fe7..869adb4f8d 100644 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/board_column.dart +++ b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/board_column.dart @@ -36,7 +36,7 @@ class BoardColumnWidget extends StatefulWidget { String get columnId => dataController.identifier; - final List acceptColumns; + final List acceptedColumns; final BoardColumnCardBuilder cardBuilder; @@ -54,7 +54,7 @@ class BoardColumnWidget extends StatefulWidget { required this.onReorder, required this.dataController, required this.phantomController, - required this.acceptColumns, + required this.acceptedColumns, this.config = const ReorderFlexConfig(), this.spacing, this.onDragStarted, @@ -89,7 +89,7 @@ class _BoardColumnWidgetState extends State { final interceptor = CrossReorderFlexDragTargetInterceptor( reorderFlexId: widget.columnId, delegate: widget.phantomController, - acceptReorderFlexIds: widget.acceptColumns, + acceptedReorderFlexIds: widget.acceptedColumns, draggableTargetBuilder: PhantomDraggableBuilder(), ); @@ -153,7 +153,7 @@ class _BoardColumnWidgetState extends State { } void _printItems(BoardColumnDataController dataController) { - String msg = ''; + String msg = 'Column${dataController.columnData} data: '; for (var element in dataController.items) { msg = '$msg$element,'; } diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/data_controller.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/data_controller.dart index b890c7da73..d8442563ed 100644 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/data_controller.dart +++ b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/board_column/data_controller.dart @@ -3,9 +3,7 @@ import 'package:flutter/material.dart'; import '../../utils/log.dart'; import '../flex/reorder_flex.dart'; -abstract class ColumnItem extends ReoderFlextItem { - String get id; - +abstract class ColumnItem extends ReoderFlexItem { bool get isPhantom => false; @override @@ -14,7 +12,8 @@ abstract class ColumnItem extends ReoderFlextItem { } } -class BoardColumnData extends ReoderFlextItem with EquatableMixin { +class BoardColumnData extends ReoderFlexItem with EquatableMixin { + @override final String id; final List items; diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/column_container.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/column_container.dart deleted file mode 100644 index e6b6a50e50..0000000000 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/column_container.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../flowy_board.dart'; -import '../rendering/board_overlay.dart'; -import 'flex/reorder_flex.dart'; - -class BoardColumnContainer extends StatefulWidget { - final ScrollController? scrollController; - final OnDragStarted? onDragStarted; - final OnReorder onReorder; - final OnDragEnded? onDragEnded; - final BoardDataController boardDataController; - final List children; - final EdgeInsets? padding; - final Widget? background; - final double spacing; - final ReorderFlexConfig config; - - const BoardColumnContainer({ - required this.boardDataController, - required this.onReorder, - required this.children, - this.onDragStarted, - this.onDragEnded, - this.scrollController, - this.padding, - this.background, - this.spacing = 0.0, - this.config = const ReorderFlexConfig(), - Key? key, - }) : super(key: key); - - @override - State createState() => _BoardColumnContainerState(); -} - -class _BoardColumnContainerState extends State { - final GlobalKey _columnContainerOverlayKey = - GlobalKey(debugLabel: '$BoardColumnContainer overlay key'); - late BoardOverlayEntry _overlayEntry; - - @override - void initState() { - _overlayEntry = BoardOverlayEntry( - builder: (BuildContext context) { - Widget reorderFlex = ReorderFlex( - key: widget.key, - scrollController: widget.scrollController, - config: widget.config, - onDragStarted: (index) {}, - onReorder: ((fromIndex, toIndex) {}), - onDragEnded: () {}, - dataSource: widget.boardDataController, - direction: Axis.horizontal, - spacing: widget.spacing, - children: widget.children, - ); - - if (widget.padding != null) { - reorderFlex = Padding( - padding: widget.padding!, - child: reorderFlex, - ); - } - return _wrapStack(reorderFlex); - }, - opaque: false); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return BoardOverlay( - key: _columnContainerOverlayKey, - initialEntries: [_overlayEntry], - ); - } - - Widget _wrapStack(Widget child) { - return Stack( - alignment: AlignmentDirectional.topStart, - children: [ - if (widget.background != null) widget.background!, - child, - ], - ); - } -} diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_state.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_state.dart index e75d1f74cd..f7b174ebee 100644 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_state.dart +++ b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_state.dart @@ -17,24 +17,32 @@ class FlexDragTargetData extends DragTargetData { Size? get draggingFeedbackSize => state.feedbackSize; - /// Indicate the dargging come from which [ReorderFlex]. + /// Indicate the dragTarget come from which [ReorderFlex]. final DraggingReorderFlex draggingReorderFlex; - ReoderFlextItem get columnItem => + final String dragTargetId; + + ReoderFlexItem get reorderFlexItem => draggingReorderFlex.itemAtIndex(draggingIndex); - String get reorderFlexId => draggingReorderFlex.id; + String get reorderFlexId => draggingReorderFlex.reorderFlexId; FlexDragTargetData({ + required this.dragTargetId, required this.draggingIndex, required this.state, required this.draggingReorderFlex, }); + + @override + String toString() { + return 'ReorderFlexId: $reorderFlexId, dragTargetId: $dragTargetId'; + } } abstract class DraggingReorderFlex { - String get id; - ReoderFlextItem itemAtIndex(int index); + String get reorderFlexId; + ReoderFlexItem itemAtIndex(int index); } class DraggingState { diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_target_inteceptor.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_target_inteceptor.dart index 8b8917bb59..4e551c057c 100644 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_target_inteceptor.dart +++ b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_target_inteceptor.dart @@ -8,12 +8,13 @@ import 'reorder_flex.dart'; abstract class ReorderFlexDragTargetInterceptor { bool canHandler(FlexDragTargetData dragTargetData); - bool onWillAccept( - BuildContext context, - ReorderFlexState reorderFlexState, - FlexDragTargetData dragTargetData, - int itemIndex, - ); + bool onWillAccept({ + required BuildContext context, + required ReorderFlexState reorderFlexState, + required FlexDragTargetData dragTargetData, + required String dragTargetId, + required int dragTargetIndex, + }); void onAccept(FlexDragTargetData dragTargetData); @@ -22,6 +23,51 @@ abstract class ReorderFlexDragTargetInterceptor { ReorderFlexDraggableTargetBuilder? get draggableTargetBuilder; } +abstract class OverlapReorderFlexDragTargetDelegate + extends CrossReorderFlexDragTargetDelegate {} + +class OverlapReorderFlexDragTargetInteceptor + extends ReorderFlexDragTargetInterceptor { + final String reorderFlexId; + final List acceptedReorderFlexId; + final OverlapReorderFlexDragTargetDelegate delegate; + + OverlapReorderFlexDragTargetInteceptor({ + required this.delegate, + required this.reorderFlexId, + required this.acceptedReorderFlexId, + }); + + @override + bool canHandler(FlexDragTargetData dragTargetData) { + return acceptedReorderFlexId.contains(dragTargetData.reorderFlexId); + } + + @override + ReorderFlexDraggableTargetBuilder? get draggableTargetBuilder => null; + + @override + void onAccept(FlexDragTargetData dragTargetData) {} + + @override + void onLeave(FlexDragTargetData dragTargetData) {} + + @override + bool onWillAccept( + {required BuildContext context, + required ReorderFlexState reorderFlexState, + required FlexDragTargetData dragTargetData, + required String dragTargetId, + required int dragTargetIndex}) { + Log.trace('dragTargetData: $dragTargetData'); + Log.trace('currentDragTargetId: $dragTargetId'); + // + Log.debug('Switch to $dragTargetId'); + + return true; + } +} + abstract class CrossReorderFlexDragTargetDelegate { bool acceptNewDragTargetData( String columnId, @@ -38,7 +84,7 @@ abstract class CrossReorderFlexDragTargetDelegate { class CrossReorderFlexDragTargetInterceptor extends ReorderFlexDragTargetInterceptor { final String reorderFlexId; - final List acceptReorderFlexIds; + final List acceptedReorderFlexIds; final CrossReorderFlexDragTargetDelegate delegate; @override final ReorderFlexDraggableTargetBuilder? draggableTargetBuilder; @@ -46,17 +92,17 @@ class CrossReorderFlexDragTargetInterceptor CrossReorderFlexDragTargetInterceptor({ required this.reorderFlexId, required this.delegate, - required this.acceptReorderFlexIds, + required this.acceptedReorderFlexIds, this.draggableTargetBuilder, }); @override bool canHandler(FlexDragTargetData dragTargetData) { - if (acceptReorderFlexIds.isEmpty) { - return true; + if (acceptedReorderFlexIds.isEmpty) { + return false; } - if (acceptReorderFlexIds.contains(dragTargetData.reorderFlexId)) { + if (acceptedReorderFlexIds.contains(dragTargetData.reorderFlexId)) { /// If the columnId equal to the dragTargetData's columnId, /// it means the dragTarget is dragging on the top of its own list. /// Otherwise, it means the dargTarget was moved to another list. @@ -66,40 +112,6 @@ class CrossReorderFlexDragTargetInterceptor } } - @override - bool onWillAccept( - BuildContext context, - ReorderFlexState reorderFlexState, - FlexDragTargetData dragTargetData, - int itemIndex, - ) { - final isNewDragTarget = delegate.acceptNewDragTargetData( - reorderFlexId, - dragTargetData, - itemIndex, - ); - - if (isNewDragTarget == false) { - delegate.updateDragTargetData( - reorderFlexId, - dragTargetData, - itemIndex, - ); - - reorderFlexState.onWillAccept( - context, - dragTargetData.draggingIndex, - itemIndex, - ); - } else { - Log.debug( - '[$CrossReorderFlexDragTargetInterceptor] move Column${dragTargetData.reorderFlexId}:${dragTargetData.draggingIndex} ' - 'to Column$reorderFlexId:$itemIndex'); - } - - return true; - } - @override void onAccept(FlexDragTargetData dragTargetData) { Log.trace( @@ -111,4 +123,38 @@ class CrossReorderFlexDragTargetInterceptor Log.trace( '[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on leave'); } + + @override + bool onWillAccept( + {required BuildContext context, + required ReorderFlexState reorderFlexState, + required FlexDragTargetData dragTargetData, + required String dragTargetId, + required int dragTargetIndex}) { + final isNewDragTarget = delegate.acceptNewDragTargetData( + reorderFlexId, + dragTargetData, + dragTargetIndex, + ); + + if (isNewDragTarget == false) { + delegate.updateDragTargetData( + reorderFlexId, + dragTargetData, + dragTargetIndex, + ); + + reorderFlexState.onWillAccept( + context, + dragTargetData.draggingIndex, + dragTargetIndex, + ); + } else { + Log.debug( + '[$CrossReorderFlexDragTargetInterceptor] move Column${dragTargetData.reorderFlexId}:${dragTargetData.draggingIndex} ' + 'to Column$reorderFlexId:$dragTargetIndex'); + } + + return true; + } } diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/reorder_flex.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/reorder_flex.dart index 4c10772571..9ed78dcdb1 100644 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/reorder_flex.dart +++ b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/reorder_flex.dart @@ -18,10 +18,12 @@ typedef OnReveivePassedInPhantom = void Function( abstract class ReoderFlextDataSource { String get identifier; - List get items; + List get items; } -abstract class ReoderFlextItem {} +abstract class ReoderFlexItem { + String get id; +} class ReorderFlexConfig { final bool needsLongPressDraggable = true; @@ -68,10 +70,10 @@ class ReorderFlex extends StatefulWidget with DraggingReorderFlex { State createState() => ReorderFlexState(); @override - String get id => dataSource.identifier; + String get reorderFlexId => dataSource.identifier; @override - ReoderFlextItem itemAtIndex(int index) { + ReoderFlexItem itemAtIndex(int index) { return dataSource.items[index]; } } @@ -90,7 +92,7 @@ class ReorderFlexState extends State @override void initState() { - dragState = DraggingState(widget.id); + dragState = DraggingState(widget.reorderFlexId); _dragAnimationController = DragAnimationController( reorderAnimationDuration: widget.config.reorderAnimationDuration, @@ -135,6 +137,7 @@ class ReorderFlexState extends State for (int i = 0; i < widget.children.length; i += 1) { Widget child = widget.children[i]; + if (widget.spacing != null) { children.add(SizedBox(width: widget.spacing!)); } @@ -283,12 +286,17 @@ class ReorderFlexState extends State } ReorderDragTarget _buildDragTarget( - BuildContext builderContext, Widget child, int childIndex) { + BuildContext builderContext, + Widget child, + int dragTargetIndex, + ) { + final ReoderFlexItem item = widget.dataSource.items[dragTargetIndex]; return ReorderDragTarget( dragTargetData: FlexDragTargetData( - draggingIndex: childIndex, + draggingIndex: dragTargetIndex, state: dragState, draggingReorderFlex: widget, + dragTargetId: item.id, ), onDragStarted: (draggingWidget, draggingIndex, size) { Log.debug("Column${widget.dataSource.identifier} start dragging"); @@ -308,15 +316,16 @@ class ReorderFlexState extends State }); }, onWillAccept: (FlexDragTargetData dragTargetData) { - assert(widget.dataSource.items.length > childIndex); + assert(widget.dataSource.items.length > dragTargetIndex); if (_interceptDragTarget( dragTargetData, (interceptor) => interceptor.onWillAccept( - builderContext, - this, - dragTargetData, - childIndex, + context: builderContext, + reorderFlexState: this, + dragTargetData: dragTargetData, + dragTargetId: item.id, + dragTargetIndex: dragTargetIndex, ), )) { return true; @@ -324,7 +333,7 @@ class ReorderFlexState extends State Log.debug( '[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, count: ${widget.dataSource.items.length}'); final dragIndex = dragTargetData.draggingIndex; - return onWillAccept(builderContext, dragIndex, childIndex); + return onWillAccept(builderContext, dragIndex, dragTargetIndex); } }, onAccept: (dragTargetData) { diff --git a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/phantom/phantom_controller.dart b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/phantom/phantom_controller.dart index d852a84fd7..005169278a 100644 --- a/frontend/app_flowy/packages/flowy_board/lib/src/widgets/phantom/phantom_controller.dart +++ b/frontend/app_flowy/packages/flowy_board/lib/src/widgets/phantom/phantom_controller.dart @@ -14,7 +14,10 @@ mixin ColumnDataPhantomMixim { BoardColumnDataController? get; } -class BoardPhantomController extends CrossReorderFlexDragTargetDelegate { +class BoardPhantomController + with + CrossReorderFlexDragTargetDelegate, + OverlapReorderFlexDragTargetDelegate { final BoardPhantomControllerDelegate delegate; PhantomRecord? phantomRecord; @@ -142,17 +145,19 @@ class BoardPhantomController extends CrossReorderFlexDragTargetDelegate { } void _removePhantom(String columnId) { - final items = delegate.controller(columnId)?.items; - if (items == null) { - return; - } + final index = delegate + .controller(columnId) + ?.items + .indexWhere((item) => item.isPhantom); + + if (index == null) return; - final index = items.indexWhere((item) => item.isPhantom); assert(index != -1); + if (index != -1) { - items.removeAt(index); + delegate.controller(columnId)?.removeAt(index); Log.debug( - '[$BoardPhantomController] Column$columnId remove phantom, current count: ${items.length}'); + '[$BoardPhantomController] Column$columnId remove phantom, current count: ${delegate.controller(columnId)?.items.length}'); columnsState.notifyDidRemovePhantom(columnId); columnsState.removeColumnListener(columnId); } @@ -195,7 +200,7 @@ class BoardPhantomController extends CrossReorderFlexDragTargetDelegate { phantomRecord = PhantomRecord( toColumnId: columnId, toColumnIndex: index, - item: dragTargetData.columnItem as ColumnItem, + item: dragTargetData.reorderFlexItem as ColumnItem, fromColumnId: dragTargetData.reorderFlexId, fromColumnIndex: dragTargetData.draggingIndex, ); @@ -275,7 +280,7 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger Widget? get draggingWidget => dragTargetData.draggingWidget; - ColumnItem get itemData => dragTargetData.columnItem as ColumnItem; + ColumnItem get itemData => dragTargetData.reorderFlexItem as ColumnItem; @override VoidCallback? onInserted;