From 254c9d948b0b4f21b95b671d5632cd72a8aa9d98 Mon Sep 17 00:00:00 2001 From: Nikita Shebalin Date: Wed, 25 Jun 2025 17:37:28 +0300 Subject: [PATCH] Add addDragStartListener option to AnimatedGridView, AnimatedListView, and related components --- lib/animated_reorderable_list.dart | 3 +++ lib/src/animated_gridview.dart | 26 +++++++++++++++++-- lib/src/animated_listview.dart | 22 ++++++++++++++++ lib/src/animated_reorderable_gridview.dart | 24 ++++++++++++++++- lib/src/animated_reorderable_listview.dart | 22 ++++++++++++++++ .../builder/reorderable_animated_builder.dart | 12 ++++++++- .../reorderable_animated_list_base.dart | 8 +++++- .../reorderable_animated_list_impl.dart | 9 +++++-- 8 files changed, 119 insertions(+), 7 deletions(-) diff --git a/lib/animated_reorderable_list.dart b/lib/animated_reorderable_list.dart index c3fd47b..28dede2 100644 --- a/lib/animated_reorderable_list.dart +++ b/lib/animated_reorderable_list.dart @@ -8,3 +8,6 @@ export 'src/animated_reorderable_listview.dart'; //Direct access to the sliver builder, circumventing the ScrollView layer export 'src/builder/reorderable_animated_list_impl.dart'; +//Direct access to the ReorderableGridDragStartListener, if required when +//addDragStartListener false +export 'src/component/drag_listener.dart'; diff --git a/lib/src/animated_gridview.dart b/lib/src/animated_gridview.dart index e018f5d..ef4df84 100644 --- a/lib/src/animated_gridview.dart +++ b/lib/src/animated_gridview.dart @@ -286,6 +286,26 @@ class AnimatedGridView extends StatefulWidget { /// Defaults to true. final bool enableSwap; + /// Whether to add a [ReorderableGridDragStartListener] to the reorderable ItemBuilder. + /// + /// Defaults to true. + /// + /// If set to false, the items in ItemBuilder will not respond to pointer down events, + /// which means they won't be draggable. This can be useful if you still want to + /// receive item pointer events, and add your custom drag start listener + /// to the item widget. + /// + /// Example: + /// ```dart + /// ReorderableGridDragStartListener( + /// index: dragIndex, + /// child: const Icon( + /// Icons.drag_handle_outlined, + /// ), + /// ), + /// ``` + final bool addDragStartListener; + /// Creates a [AnimatedGridView] that animates insertion and removal of the item. const AnimatedGridView( {Key? key, @@ -311,7 +331,8 @@ class AnimatedGridView extends StatefulWidget { this.removeItemBuilder, this.shrinkWrap = false, required this.isSameItem, - this.enableSwap = true}) + this.enableSwap = true, + this.addDragStartListener = true}) : super(key: key); /// The state from the closest instance of this class that encloses the given @@ -392,7 +413,8 @@ class AnimatedGridViewState insertItemBuilder: widget.insertItemBuilder, removeItemBuilder: widget.removeItemBuilder, isSameItem: widget.isSameItem, - enableSwap: widget.enableSwap), + enableSwap: widget.enableSwap, + addDragStartListener: widget.addDragStartListener), ), ]); } diff --git a/lib/src/animated_listview.dart b/lib/src/animated_listview.dart index f5375fd..9548f13 100644 --- a/lib/src/animated_listview.dart +++ b/lib/src/animated_listview.dart @@ -283,6 +283,26 @@ class AnimatedListView extends StatefulWidget { /// Defaults to true. final bool enableSwap; + /// Whether to add a [ReorderableGridDragStartListener] to the reorderable ItemBuilder. + /// + /// Defaults to true. + /// + /// If set to false, the items in ItemBuilder will not respond to pointer down events, + /// which means they won't be draggable. This can be useful if you still want to + /// receive item pointer events, and add your custom drag start listener + /// to the item widget. + /// + /// Example: + /// ```dart + /// ReorderableGridDragStartListener( + /// index: dragIndex, + /// child: const Icon( + /// Icons.drag_handle_outlined, + /// ), + /// ), + /// ``` + final bool addDragStartListener; + /// Creates a [AnimatedListView] that animates insertion and removal of the item. const AnimatedListView({ Key? key, @@ -308,6 +328,7 @@ class AnimatedListView extends StatefulWidget { this.shrinkWrap = false, required this.isSameItem, this.enableSwap = true, + this.addDragStartListener = true, }) : super(key: key); /// The state from the closest instance of this class that encloses the given @@ -388,6 +409,7 @@ class AnimatedListViewState removeItemBuilder: widget.removeItemBuilder, isSameItem: widget.isSameItem, enableSwap: widget.enableSwap, + addDragStartListener: widget.addDragStartListener, ), ), ]); diff --git a/lib/src/animated_reorderable_gridview.dart b/lib/src/animated_reorderable_gridview.dart index ab74536..641618c 100644 --- a/lib/src/animated_reorderable_gridview.dart +++ b/lib/src/animated_reorderable_gridview.dart @@ -347,6 +347,26 @@ class AnimatedReorderableGridView extends StatefulWidget { /// Defaults to true. final bool enableSwap; + /// Whether to add a [ReorderableGridDragStartListener] to the reorderable ItemBuilder. + /// + /// Defaults to true. + /// + /// If set to false, the items in ItemBuilder will not respond to pointer down events, + /// which means they won't be draggable. This can be useful if you still want to + /// receive item pointer events, and add your custom drag start listener + /// to the item widget. + /// + /// Example: + /// ```dart + /// ReorderableGridDragStartListener( + /// index: dragIndex, + /// child: const Icon( + /// Icons.drag_handle_outlined, + /// ), + /// ), + /// ``` + final bool addDragStartListener; + /// Creates a [AnimatedReorderableGridView] that enables users to interactively reorder items through dragging, /// with animated insertion and removal of items. const AnimatedReorderableGridView( @@ -381,7 +401,8 @@ class AnimatedReorderableGridView extends StatefulWidget { this.dragStartDelay = const Duration(milliseconds: 500), this.nonDraggableItems = const [], this.lockedItems = const [], - this.enableSwap = true}) + this.enableSwap = true, + this.addDragStartListener = true}) : super(key: key); /// The state from the closest instance of this class that encloses the given @@ -473,6 +494,7 @@ class AnimatedReorderableGridViewState nonDraggableItems: widget.nonDraggableItems, lockedItems: widget.lockedItems, enableSwap: widget.enableSwap, + addDragStartListener: widget.addDragStartListener, ), ), ]); diff --git a/lib/src/animated_reorderable_listview.dart b/lib/src/animated_reorderable_listview.dart index 9dc03b5..8c5fb16 100644 --- a/lib/src/animated_reorderable_listview.dart +++ b/lib/src/animated_reorderable_listview.dart @@ -365,6 +365,26 @@ class AnimatedReorderableListView extends StatefulWidget { /// Defaults to true. final bool enableSwap; + /// Whether to add a [ReorderableGridDragStartListener] to the reorderable ItemBuilder. + /// + /// Defaults to true. + /// + /// If set to false, the items in ItemBuilder will not respond to pointer down events, + /// which means they won't be draggable. This can be useful if you still want to + /// receive item pointer events, and add your custom drag start listener + /// to the item widget. + /// + /// Example: + /// ```dart + /// ReorderableGridDragStartListener( + /// index: dragIndex, + /// child: const Icon( + /// Icons.drag_handle_outlined, + /// ), + /// ), + /// ``` + final bool addDragStartListener; + /// Creates a [AnimatedReorderableListView] that enables users to interactively reorder items through dragging, /// with animated insertion and removal of items. const AnimatedReorderableListView({ @@ -400,6 +420,7 @@ class AnimatedReorderableListView extends StatefulWidget { this.nonDraggableItems = const [], this.lockedItems = const [], this.enableSwap = true, + this.addDragStartListener = true, }) : super(key: key); /// The state from the closest instance of this class that encloses the given @@ -491,6 +512,7 @@ class AnimatedReorderableListViewState nonDraggableItems: widget.nonDraggableItems, lockedItems: widget.lockedItems, enableSwap: widget.enableSwap, + addDragStartListener: widget.addDragStartListener, ), ), ]); diff --git a/lib/src/builder/reorderable_animated_builder.dart b/lib/src/builder/reorderable_animated_builder.dart index 5e45869..51153fd 100644 --- a/lib/src/builder/reorderable_animated_builder.dart +++ b/lib/src/builder/reorderable_animated_builder.dart @@ -32,6 +32,7 @@ class ReorderableAnimatedBuilder extends StatefulWidget { final Duration dragStartDelay; final List nonDraggableIndices; final List lockedIndices; + final bool addDragStartListener; const ReorderableAnimatedBuilder( {Key? key, @@ -49,7 +50,8 @@ class ReorderableAnimatedBuilder extends StatefulWidget { this.longPressDraggable = false, required this.dragStartDelay, required this.nonDraggableIndices, - required this.lockedIndices}) + required this.lockedIndices, + required this.addDragStartListener}) : assert(initialCount >= 0), super(key: key); @@ -848,6 +850,14 @@ class ReorderableAnimatedBuilderState extends State return true; }()); final Key itemGlobalKey = _MotionBuilderItemGlobalKey(item.key!, this); + + if (!widget.addDragStartListener) { + return SizedBox( + key: itemGlobalKey, + child: itemWithSemantics, + ); + } + if (widget.buildDefaultDragHandles) { switch (Theme.of(context).platform) { case TargetPlatform.linux: diff --git a/lib/src/builder/reorderable_animated_list_base.dart b/lib/src/builder/reorderable_animated_list_base.dart index d1288dc..14c97c3 100644 --- a/lib/src/builder/reorderable_animated_list_base.dart +++ b/lib/src/builder/reorderable_animated_list_base.dart @@ -38,6 +38,7 @@ abstract class ReorderableAnimatedListBase final List nonDraggableItems; final List lockedItems; final bool enableSwap; + final bool addDragStartListener; const ReorderableAnimatedListBase( {Key? key, @@ -61,7 +62,8 @@ abstract class ReorderableAnimatedListBase this.dragStartDelay, this.enableSwap = true, required this.nonDraggableItems, - required this.lockedItems}) + required this.lockedItems, + this.addDragStartListener = true}) : assert(itemBuilder != null), super(key: key); } @@ -179,6 +181,10 @@ abstract class ReorderableAnimatedListBaseState< .map((entry) => entry.key) .toList(); + @nonVirtual + @protected + bool get addDragStartListener => widget.addDragStartListener; + @override void initState() { super.initState(); diff --git a/lib/src/builder/reorderable_animated_list_impl.dart b/lib/src/builder/reorderable_animated_list_impl.dart index 7a5701b..9f3ac86 100644 --- a/lib/src/builder/reorderable_animated_list_impl.dart +++ b/lib/src/builder/reorderable_animated_list_impl.dart @@ -28,6 +28,7 @@ class ReorderableAnimatedListImpl List nonDraggableItems = const [], List lockedItems = const [], bool enableSwap = true, + bool addDragStartListener = true, }) : super( key: key, items: items, @@ -49,7 +50,8 @@ class ReorderableAnimatedListImpl dragStartDelay: dragStartDelay, nonDraggableItems: nonDraggableItems, lockedItems: lockedItems, - enableSwap: enableSwap); + enableSwap: enableSwap, + addDragStartListener: addDragStartListener); const ReorderableAnimatedListImpl.grid({ Key? key, @@ -74,6 +76,7 @@ class ReorderableAnimatedListImpl List nonDraggableItems = const [], List lockedItems = const [], bool enableSwap = true, + bool addDragStartListener = true, }) : super( key: key, items: items, @@ -96,7 +99,8 @@ class ReorderableAnimatedListImpl dragStartDelay: dragStartDelay, nonDraggableItems: nonDraggableItems, lockedItems: lockedItems, - enableSwap: enableSwap); + enableSwap: enableSwap, + addDragStartListener: addDragStartListener); @override ReorderableAnimatedListImplState createState() => @@ -127,6 +131,7 @@ class ReorderableAnimatedListImplState dragStartDelay: dragStartDelay, nonDraggableIndices: nonDraggableItems, lockedIndices: lockedIndices, + addDragStartListener: addDragStartListener, ); } }