Documentation menu
Concepts

Collision detection

When a draggable overlaps several droppables, a collision detector decides which one wins. Detection is shared math in the engine, so Flutter and Jaspr resolve the same target from the same input.

You'll learn
  • The built-in collision detectors and when to use each.
  • How to set a detector on a scope.
  • How to combine detectors into a fallback chain.

Built-in detectors

All detectors live on DndCollisionDetectors:

  • closestCenter — the target whose center is nearest the pointer. A solid default for grids and free layouts.
  • closestCorners — compares corners instead of centers; steadier when targets vary a lot in size.
  • rectIntersection — the target the dragged rect overlaps most. Natural for list and Kanban reordering.
  • pointerWithin — only targets the pointer is literally inside. Precise, but needs a pointer (not keyboard) drag.

Candidates never include the item being dragged, so a sortable item cannot win its own collision. The dragged id is still passed along as DndCollisionInput.activeId, which lets a custom detector scope candidates by kind when two sortable surfaces share one controller — see the recipes.

Setting a detector

Pass a detector to the DndController on your DndScope. The default is closestCenter.

scope.dart
DndScope(
  controller: DndController(
    collisionDetector: DndCollisionDetectors.closestCenter,
  ),
  child: board,
)

Composing

Use compose to try detectors in order and take the first that finds a target — for example, prefer a precise pointer hit, then fall back to overlap.

scope.dart
DndController(
  collisionDetector: DndCollisionDetectors.compose([
    DndCollisionDetectors.pointerWithin,
    DndCollisionDetectors.rectIntersection,
  ]),
)