computeTargetIndex static method
Computes the target item index based on drag position and velocity.
Uses velocity-based snapping for iOS-style flick gestures:
- High velocity: Project forward and snap to the projected item
- Low velocity: Snap to nearest item
This creates the satisfying "flick to jump" behavior seen in iOS.
Parameters:
currentRelativeX: Current position in 0-1 rangevelocityX: Horizontal velocity in relative unitsitemWidth: Width of each item as a fraction (1 / itemCount)itemCount: Total number of itemsvelocityThreshold: Velocity threshold for flick detection (default: 0.5)projectionTime: Time to project velocity forward in seconds (default: 0.3)
Returns: The target item index (0-based).
Example:
final targetIndex = DraggableIndicatorPhysics.computeTargetIndex(
currentRelativeX: 0.4,
velocityX: 2.0, // Fast swipe right
itemWidth: 1.0 / 3,
itemCount: 3,
);
// Returns 2 (jumped to last item due to high velocity)
Implementation
static int computeTargetIndex({
required double currentRelativeX,
required double velocityX,
required double itemWidth,
required int itemCount,
double velocityThreshold = 0.5,
double projectionTime = 0.3,
}) {
// Handle overdrag scenarios
if (currentRelativeX < 0) return 0;
if (currentRelativeX > 1) return itemCount - 1;
if (velocityX.abs() > velocityThreshold) {
// High velocity - project where we would end up
final projectedX =
(currentRelativeX + velocityX * projectionTime).clamp(0.0, 1.0);
var targetIndex =
(projectedX / itemWidth).round().clamp(0, itemCount - 1);
// Ensure we move at least one item with strong velocity
final currentIndex =
(currentRelativeX / itemWidth).round().clamp(0, itemCount - 1);
if (velocityX > velocityThreshold &&
targetIndex <= currentIndex &&
currentIndex < itemCount - 1) {
targetIndex = currentIndex + 1;
} else if (velocityX < -velocityThreshold &&
targetIndex >= currentIndex &&
currentIndex > 0) {
targetIndex = currentIndex - 1;
}
return targetIndex;
}
// Low velocity - snap to nearest
return (currentRelativeX / itemWidth).round().clamp(0, itemCount - 1);
}