Unverified Commit eef4220a authored by Dan Field's avatar Dan Field Committed by GitHub

Make large jumpTo recommend deferred loading (#64271)

parent 8489d460
......@@ -145,6 +145,24 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
double get maxScrollExtent => _maxScrollExtent;
double _maxScrollExtent;
/// The additional velocity added for a [forcePixels] change in a single
/// frame.
///
/// This value is used by [recommendDeferredLoading] in addition to the
/// [activity]'s [ScrollActivity.velocity] to ask the [physics] whether or
/// not to defer loading. It accounts for the fact that a [forcePixels] call
/// may involve a [ScrollActivity] with 0 velocity, but the scrollable is
/// still instantaneously moving from its current position to a potentially
/// very far position, and which is of interest to callers of
/// [recommendDeferredLoading].
///
/// For example, if a scrollable is currently at 5000 pixels, and we [jumpTo]
/// 0 to get back to the top of the list, we would have an implied velocity of
/// -5000 and an `activity.velocity` of 0. The jump may be going past a
/// number of resource intensive widgets which should avoid doing work if the
/// position jumps past them.
double _impliedVelocity = 0;
@override
double get pixels => _pixels;
double _pixels;
......@@ -343,8 +361,13 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
@protected
void forcePixels(double value) {
assert(pixels != null);
assert(value != null);
_impliedVelocity = value - _pixels;
_pixels = value;
notifyListeners();
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
_impliedVelocity = 0;
});
}
/// Called whenever scrolling ends, to store the current scroll offset in a
......@@ -834,7 +857,12 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
assert(context != null);
assert(activity != null);
assert(activity.velocity != null);
return physics.recommendDeferredLoading(activity.velocity, copyWith(), context);
assert(_impliedVelocity != null);
return physics.recommendDeferredLoading(
activity.velocity + _impliedVelocity,
copyWith(),
context,
);
}
@override
......
......@@ -229,4 +229,41 @@ void main() {
);
expect(controller.position.pixels, equals(0.0));
});
testWidgets('jumpTo recomends deferred loading', (WidgetTester tester) async {
int loadedWithDeferral = 0;
int buildCount = 0;
const double height = 500;
await tester.pumpWidget(MaterialApp(
home: ListView.builder(
itemBuilder: (BuildContext context, int index) {
buildCount += 1;
if (Scrollable.recommendDeferredLoadingForContext(context)) {
loadedWithDeferral += 1;
}
return const SizedBox(height: height);
},
),
));
// The two visible on screen should have loaded without deferral.
expect(buildCount, 2);
expect(loadedWithDeferral, 0);
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
position.jumpTo(height * 100);
await tester.pump();
// All but the first two that were loaded normally should have gotten a
// recommendation to defer.
expect(buildCount, 102);
expect(loadedWithDeferral, 100);
position.jumpTo(height * 102);
await tester.pump();
// The smaller jump should not have recommended deferral.
expect(buildCount, 104);
expect(loadedWithDeferral, 100);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment