Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ class MyApp extends StatefulWidget {
}

class _MyAppState extends State<MyApp> {
static const _startCounter = 200;
static const _startCounter = 7;

final lockedIndices = <int>[0, 4];
final nonDraggableIndices = [0, 2, 3];
final lockedIndices = <int>[];
final nonDraggableIndices = <int>[];

int keyCounter = _startCounter;
List<int> children = List.generate(_startCounter, (index) => index);
Expand Down
67 changes: 55 additions & 12 deletions lib/controller/reorderable_drag_and_drop_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,28 @@ class ReorderableDragAndDropController extends ReorderableController {
/// After dragging a child, [scrollOffset] is always updated.
Offset scrollOffset = Offset.zero;

///
Axis _scrollDirection = Axis.vertical;

///
bool _reverse = false;

void handleDragStarted({
required ReorderableEntity reorderableEntity,
required Offset currentScrollOffset,
required List<int> lockedIndices,
required bool isScrollableOutside,
required Axis scrollDirection,
required bool reverse,
}) {
_releasedReorderableEntity = null;
this.lockedIndices = lockedIndices;
super.draggedEntity = childrenKeyMap[reorderableEntity.key.value];
scrollOffset = currentScrollOffset;
this.isScrollableOutside = isScrollableOutside;
startDraggingScrollOffset = currentScrollOffset;
_scrollDirection = scrollDirection;
_reverse = reverse;
}

bool handleDragUpdate({required PointerMoveEvent pointerMoveEvent}) {
Expand All @@ -76,7 +86,7 @@ class ReorderableDragAndDropController extends ReorderableController {
if (collisionOrderId != null && !lockedIndices.contains(collisionOrderId)) {
final draggedOrderId = super.draggedEntity!.updatedOrderId;
final difference = draggedOrderId - collisionOrderId;

print('difference $difference');
if (difference > 1 || difference < -1) {
// print('_draggedEntity $_draggedEntity');
}
Expand Down Expand Up @@ -220,25 +230,58 @@ class ReorderableDragAndDropController extends ReorderableController {
required dynamic keyValue,
required Offset draggedOffset,
}) {
final result = _isDraggingOutOfArea(draggedOffset: draggedOffset);

if (result != null) return result;

for (final entry in childrenKeyMap.entries) {
final localPosition = entry.value.updatedOffset;
final size = entry.value.size;
if (entry.key == keyValue) continue;

if (entry.key == keyValue) {
continue;
}
final reorderableEntity = entry.value;

// checking collision with full item size and local position
if (draggedOffset.dx >= localPosition.dx &&
draggedOffset.dy >= localPosition.dy &&
draggedOffset.dx <= localPosition.dx + size.width &&
draggedOffset.dy <= localPosition.dy + size.height) {
return entry.value;
if (draggedOffset.dx >= reorderableEntity.minDx &&
draggedOffset.dy >= reorderableEntity.minDy &&
draggedOffset.dx <= reorderableEntity.maxDx &&
draggedOffset.dy <= reorderableEntity.maxDy) {
return reorderableEntity;
}
}
return null;
}

// todo muss noch geprüft werden auf horizontales scrolling
/// Checks if the dragged item is outside the reorderable area.
///
/// This method determines if the dragged item, based on its `draggedOffset`,
/// is outside the valid reorderable bounds. It accounts for the scroll
/// direction and list order (reversed or not).
///
/// Returns the `lastItem` if the item is out of bounds, otherwise returns
/// `null`.
ReorderableEntity? _isDraggingOutOfArea({
required Offset draggedOffset,
}) {
final isVertical = _scrollDirection == Axis.vertical;

final firstItem = childrenOrderMap[0]!;
final lastItem = childrenOrderMap[childrenKeyMap.length - 1]!;

final firstPosition = isVertical
? (_reverse ? firstItem.maxDy : firstItem.minDy)
: (_reverse ? firstItem.maxDx : firstItem.minDx);
final lastPosition = isVertical
? (_reverse ? lastItem.minDy : lastItem.maxDy)
: (_reverse ? lastItem.minDx : lastItem.maxDx);

final dragPosition = isVertical ? draggedOffset.dy : draggedOffset.dx;

final isOutOfArea = _reverse
? (dragPosition < lastPosition || dragPosition > firstPosition)
: (dragPosition > lastPosition || dragPosition < firstPosition);

return isOutOfArea ? lastItem : null;
}

/// Returns a list of all updated positions containing old and new index.
///
/// This method is a special case because of [widget.lockedIndices]. To ensure
Expand Down
8 changes: 8 additions & 0 deletions lib/entities/reorderable_entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,12 @@ class ReorderableEntity {
);

bool get isNew => originalOrderId == _isNewChildId;

double get minDx => updatedOffset.dx;

double get minDy => updatedOffset.dy;

double get maxDx => updatedOffset.dx + size.width;

double get maxDy => updatedOffset.dy + size.height;
}
13 changes: 13 additions & 0 deletions lib/widgets/reorderable_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,25 @@ class _ReorderableBuilderState extends State<ReorderableBuilder>
currentScrollOffset: _getScrollOffset(),
lockedIndices: widget.lockedIndices,
isScrollableOutside: _isScrollOutside,
scrollDirection: _scrollDirection,
reverse: widget.reverse,
);
widget.onDragStarted?.call(reorderableEntity.updatedOrderId);

setState(() {});
}

// todo: diese nützlichen zugriffe sollten iwo zentral gelagert werden, z.B. ein ScrollControllerHelper
Axis get _scrollDirection {
final axis = widget.scrollController?.position.axis;

if (axis == null) {
return Scrollable.of(context).position.axis;
} else {
return axis;
}
}

void _handleDragUpdate(PointerMoveEvent pointerMoveEvent) {
final hasUpdated = _reorderableController.handleDragUpdate(
pointerMoveEvent: pointerMoveEvent,
Expand Down
Loading