createBallisticSimulation method
- @override
override
Returns a simulation for ballistic scrolling starting from the given position with the given velocity.
This is used by ScrollPositionWithSingleContext in the ScrollPositionWithSingleContext.goBallistic method. If the result is non-null, ScrollPositionWithSingleContext will begin a BallisticScrollActivity with the returned value. Otherwise, it will begin an idle activity instead.
The given position
is only valid during this method call. Do not keep a
reference to it to use later, as the values may update, may not update, or
may update to reflect an entirely unrelated scrollable.
Implementation
@override
Simulation createBallisticSimulation(ScrollMetrics position, double velocity) {
assert(
position is _FixedExtentScrollPosition,
'FixedExtentScrollPhysics can only be used with Scrollables that uses '
'the FixedExtentScrollController'
);
final _FixedExtentScrollPosition metrics = position;
// Scenario 1:
// If we're out of range and not headed back in range, defer to the parent
// ballistics, which should put us back in range at the scrollable's boundary.
if ((velocity <= 0.0 && metrics.pixels <= metrics.minScrollExtent) ||
(velocity >= 0.0 && metrics.pixels >= metrics.maxScrollExtent)) {
return super.createBallisticSimulation(metrics, velocity);
}
// Create a test simulation to see where it would have ballistically fallen
// naturally without settling onto items.
final Simulation testFrictionSimulation =
super.createBallisticSimulation(metrics, velocity);
// Scenario 2:
// If it was going to end up past the scroll extent, defer back to the
// parent physics' ballistics again which should put us on the scrollable's
// boundary.
if (testFrictionSimulation != null
&& (testFrictionSimulation.x(double.infinity) == metrics.minScrollExtent
|| testFrictionSimulation.x(double.infinity) == metrics.maxScrollExtent)) {
return super.createBallisticSimulation(metrics, velocity);
}
// From the natural final position, find the nearest item it should have
// settled to.
final int settlingItemIndex = _getItemFromOffset(
offset: testFrictionSimulation?.x(double.infinity) ?? metrics.pixels,
itemExtent: metrics.itemExtent,
minScrollExtent: metrics.minScrollExtent,
maxScrollExtent: metrics.maxScrollExtent,
);
final double settlingPixels = settlingItemIndex * metrics.itemExtent;
// Scenario 3:
// If there's no velocity and we're already at where we intend to land,
// do nothing.
if (velocity.abs() < tolerance.velocity
&& (settlingPixels - metrics.pixels).abs() < tolerance.distance) {
return null;
}
// Scenario 4:
// If we're going to end back at the same item because initial velocity
// is too low to break past it, use a spring simulation to get back.
if (settlingItemIndex == metrics.itemIndex) {
return SpringSimulation(
spring,
metrics.pixels,
settlingPixels,
velocity,
tolerance: tolerance,
);
}
// Scenario 5:
// Create a new friction simulation except the drag will be tweaked to land
// exactly on the item closest to the natural stopping point.
return FrictionSimulation.through(
metrics.pixels,
settlingPixels,
velocity,
tolerance.velocity * velocity.sign,
);
}