@@ -145,6 +145,7 @@ void main() {
145
145
}
146
146
147
147
late MessageListScrollController controller;
148
+ late MessageListScrollPosition position;
148
149
149
150
Future <void > prepare (WidgetTester tester, {
150
151
bool reuseController = false ,
@@ -158,6 +159,7 @@ void main() {
158
159
child: buildList (controller: controller,
159
160
topHeight: topHeight, bottomHeight: bottomHeight)));
160
161
await tester.pump ();
162
+ position = controller.position;
161
163
}
162
164
163
165
// The `skipOffstage: false` produces more informative output
@@ -310,6 +312,71 @@ void main() {
310
312
// … and check the scroll position is preserved, not reset to initial.
311
313
check (tester.getRect (findTop)).bottom.equals (400 );
312
314
});
315
+
316
+ group ('scrollToEnd' , () {
317
+ testWidgets ('short -> slow' , (tester) async {
318
+ await prepare (tester, topHeight: 300 , bottomHeight: 600 );
319
+ await tester.drag (findBottom, Offset (0 , 300 ));
320
+ await tester.pump ();
321
+ check (position.extentAfter).equals (300 );
322
+
323
+ // Start scrolling to end, from just a short distance up.
324
+ position.scrollToEnd ();
325
+ await tester.pump ();
326
+ check (position.extentAfter).equals (300 );
327
+ check (position.activity).isA <DrivenScrollActivity >();
328
+
329
+ // The scrolling moves at a stately pace; …
330
+ await tester.pump (Duration (milliseconds: 100 ));
331
+ check (position.extentAfter).equals (200 );
332
+
333
+ await tester.pump (Duration (milliseconds: 100 ));
334
+ check (position.extentAfter).equals (100 );
335
+
336
+ // … then upon reaching the end, …
337
+ await tester.pump (Duration (milliseconds: 100 ));
338
+ check (position.extentAfter).equals (0 );
339
+
340
+ // … goes idle on the next frame, …
341
+ await tester.pump (Duration (milliseconds: 1 ));
342
+ check (position.activity).isA <IdleScrollActivity >();
343
+ // … without moving any farther.
344
+ check (position.extentAfter).equals (0 );
345
+ });
346
+
347
+ testWidgets ('long -> bounded speed' , (tester) async {
348
+ const referenceSpeed = 8000.0 ;
349
+ const seconds = 10 ;
350
+ const distance = seconds * referenceSpeed;
351
+ await prepare (tester, topHeight: distance + 1000 , bottomHeight: 300 );
352
+ await tester.drag (findBottom, Offset (0 , distance));
353
+ await tester.pump ();
354
+ check (position.extentAfter).equals (distance);
355
+
356
+ // Start scrolling to end.
357
+ position.scrollToEnd ();
358
+ await tester.pump ();
359
+ check (position.activity).isA <DrivenScrollActivity >();
360
+
361
+ // Let it scroll, plotting the trajectory.
362
+ final log = < double > [];
363
+ for (int i = 0 ; i < seconds; i++ ) {
364
+ log.add (position.extentAfter);
365
+ await tester.pump (const Duration (seconds: 1 ));
366
+ }
367
+ log.add (position.extentAfter);
368
+ check (log).deepEquals (List .generate (seconds + 1 ,
369
+ (i) => distance - referenceSpeed * i));
370
+
371
+ // Having reached the end, …
372
+ check (position.extentAfter).equals (0 );
373
+ // … it goes idle on the next frame, …
374
+ await tester.pump (Duration (milliseconds: 1 ));
375
+ check (position.activity).isA <IdleScrollActivity >();
376
+ // … without moving any farther.
377
+ check (position.extentAfter).equals (0 );
378
+ });
379
+ });
313
380
});
314
381
}
315
382
0 commit comments