Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit eef4220

Browse files
authored
Make large jumpTo recommend deferred loading (#64271)
1 parent 8489d46 commit eef4220

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

packages/flutter/lib/src/widgets/scroll_position.dart

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,24 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
145145
double get maxScrollExtent => _maxScrollExtent;
146146
double _maxScrollExtent;
147147

148+
/// The additional velocity added for a [forcePixels] change in a single
149+
/// frame.
150+
///
151+
/// This value is used by [recommendDeferredLoading] in addition to the
152+
/// [activity]'s [ScrollActivity.velocity] to ask the [physics] whether or
153+
/// not to defer loading. It accounts for the fact that a [forcePixels] call
154+
/// may involve a [ScrollActivity] with 0 velocity, but the scrollable is
155+
/// still instantaneously moving from its current position to a potentially
156+
/// very far position, and which is of interest to callers of
157+
/// [recommendDeferredLoading].
158+
///
159+
/// For example, if a scrollable is currently at 5000 pixels, and we [jumpTo]
160+
/// 0 to get back to the top of the list, we would have an implied velocity of
161+
/// -5000 and an `activity.velocity` of 0. The jump may be going past a
162+
/// number of resource intensive widgets which should avoid doing work if the
163+
/// position jumps past them.
164+
double _impliedVelocity = 0;
165+
148166
@override
149167
double get pixels => _pixels;
150168
double _pixels;
@@ -343,8 +361,13 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
343361
@protected
344362
void forcePixels(double value) {
345363
assert(pixels != null);
364+
assert(value != null);
365+
_impliedVelocity = value - _pixels;
346366
_pixels = value;
347367
notifyListeners();
368+
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
369+
_impliedVelocity = 0;
370+
});
348371
}
349372

350373
/// Called whenever scrolling ends, to store the current scroll offset in a
@@ -834,7 +857,12 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
834857
assert(context != null);
835858
assert(activity != null);
836859
assert(activity.velocity != null);
837-
return physics.recommendDeferredLoading(activity.velocity, copyWith(), context);
860+
assert(_impliedVelocity != null);
861+
return physics.recommendDeferredLoading(
862+
activity.velocity + _impliedVelocity,
863+
copyWith(),
864+
context,
865+
);
838866
}
839867

840868
@override

packages/flutter/test/widgets/scroll_position_test.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,41 @@ void main() {
229229
);
230230
expect(controller.position.pixels, equals(0.0));
231231
});
232+
233+
testWidgets('jumpTo recomends deferred loading', (WidgetTester tester) async {
234+
int loadedWithDeferral = 0;
235+
int buildCount = 0;
236+
const double height = 500;
237+
await tester.pumpWidget(MaterialApp(
238+
home: ListView.builder(
239+
itemBuilder: (BuildContext context, int index) {
240+
buildCount += 1;
241+
if (Scrollable.recommendDeferredLoadingForContext(context)) {
242+
loadedWithDeferral += 1;
243+
}
244+
return const SizedBox(height: height);
245+
},
246+
),
247+
));
248+
249+
// The two visible on screen should have loaded without deferral.
250+
expect(buildCount, 2);
251+
expect(loadedWithDeferral, 0);
252+
253+
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
254+
position.jumpTo(height * 100);
255+
await tester.pump();
256+
257+
// All but the first two that were loaded normally should have gotten a
258+
// recommendation to defer.
259+
expect(buildCount, 102);
260+
expect(loadedWithDeferral, 100);
261+
262+
position.jumpTo(height * 102);
263+
await tester.pump();
264+
265+
// The smaller jump should not have recommended deferral.
266+
expect(buildCount, 104);
267+
expect(loadedWithDeferral, 100);
268+
});
232269
}

0 commit comments

Comments
 (0)