resolveTarget method

TreeDropTarget<TKey>? resolveTarget({
  1. required PointerSample? sample,
  2. required TreeDropTarget<TKey>? previous,
})

Finds the live row under the probe, a pinned header first (ReorderRenderPort.findPinnedRowAtPaintedY) and otherwise the row painted there (ReorderRenderPort.findRowAtPaintedY), and hands classification to the DropZoneResolver. Pending-deletion rows are vanishing and cannot be valid drop targets; both lookups skip them. While the probe is inside a pinned band the resolved slot is the header's, so gapVisibleIndex points at the header's STRUCTURAL visible index, which may be scrolled off-screen and the make-room gap opens where the user cannot see it: the same trade hit-testing takes, and strictly better than resolving against an unrelated row.

One probe for the whole resolution: row lookup, zone classification and dwell all read sample.sliverY + probeDy (the proxy midpoint when the midpoint probe is on, the raw pointer otherwise). The x-depth hint stays on pointer x, which the vertical shift does not affect.

A null sample means the scrollable is gone: returns previous unchanged (no event can change what the user sees anyway; teardown paths handle the session's end).

The make-room gap. A probe ABOVE the hovered row's painted band cannot happen while rows are contiguous, so it means exactly one thing: the probe is inside the gap the preview has opened, and the row the scan answered with is the one BELOW that gap (the scans hit on scrollY < painted + extent, with no lower bound). The gap IS the slot previous is previewing, so that is the honest answer; re-classifying against the row below it is what made a downward "into" drag commit as a sibling. The x-depth hint is deliberately not re-evaluated there: a gap has no row to anchor a candidate chain on, so horizontal movement changes nothing until the pointer leaves the gap.

Implementation

TreeDropTarget<TKey>? resolveTarget({
  required PointerSample? sample,
  required TreeDropTarget<TKey>? previous,
}) {
  if (sample == null) {
    return previous;
  }
  final probeY = sample.sliverY + _probeDy;

  // The pinned band is on top (L21): a probe inside it is on the
  // header, not on the content scrolled beneath the strip, the priority
  // hit-testing already gives pinned headers. The gap branch below is
  // unaffected: a pinned hit never satisfies `probeY < paintedOffset`.
  final hovered =
      _renderPort.findPinnedRowAtPaintedY(probeY) ??
      _renderPort.findRowAtPaintedY(probeY);
  if (hovered == null) {
    return null;
  }

  // See "The make-room gap" above. The index equality is what keeps
  // this to the preview's own gap: the row directly below the gap is
  // the row at `gapVisibleIndex` in the current visible order, whether
  // the gap sits before the dragged block (rows at/after it shifted
  // down) or after it (rows in `[draggedEnd, gapIndex)` shifted up).
  // Any other above-the-band probe (a FLIP slide in flight, an
  // edge-ghost base, a stale `previous` after an external mutation)
  // falls through to normal classification.
  if (previous != null &&
      probeY < hovered.paintedOffset &&
      previous.gapVisibleIndex ==
          _resolver.treeController.getVisibleIndex(hovered.key)) {
    return previous;
  }

  return _resolver.resolve(
    draggedKey: _draggedKey,
    targetKey: hovered.key,
    targetPaintedY: hovered.paintedOffset,
    targetExtent: hovered.extent,
    pointerY: probeY,
    preferredDepth: _depthForPointerX?.call(sample.sliverX),
  );
}