Cancel DraggableScrollableSheet ballistic animation if a new activity begins (#86614)

This commit is contained in:
Casey Rogers 2021-07-20 16:05:33 -07:00 committed by GitHub
parent 3d47dd851d
commit 4ab29de67f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 1 deletions

View file

@ -10,6 +10,7 @@ import 'framework.dart';
import 'inherited_notifier.dart';
import 'layout_builder.dart';
import 'notification_listener.dart';
import 'scroll_activity.dart';
import 'scroll_context.dart';
import 'scroll_controller.dart';
import 'scroll_notification.dart';
@ -431,9 +432,17 @@ class _DraggableScrollableSheetScrollPosition
);
VoidCallback? _dragCancelCallback;
VoidCallback? _ballisticCancelCallback;
final _DraggableSheetExtent extent;
bool get listShouldScroll => pixels > 0.0;
@override
void beginActivity(ScrollActivity? newActivity) {
// Cancel the running ballistic simulation, if there is one.
_ballisticCancelCallback?.call();
super.beginActivity(newActivity);
}
@override
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
// We need to provide some extra extent if we haven't yet reached the max or
@ -481,6 +490,9 @@ class _DraggableScrollableSheetScrollPosition
debugLabel: objectRuntimeType(this, '_DraggableScrollableSheetPosition'),
vsync: context.vsync,
);
// Stop the ballistic animation if a new activity starts.
// See: [beginActivity].
_ballisticCancelCallback = ballisticController.stop;
double lastDelta = 0;
void _tick() {
final double delta = ballisticController.value - lastDelta;
@ -501,7 +513,10 @@ class _DraggableScrollableSheetScrollPosition
ballisticController
..addListener(_tick)
..animateWith(simulation).whenCompleteOrCancel(
ballisticController.dispose,
() {
_ballisticCancelCallback = null;
ballisticController.dispose();
},
);
}

View file

@ -264,6 +264,40 @@ void main() {
expect(find.text('Item 70'), findsNothing);
}, variant: TargetPlatformVariant.all());
testWidgets('Ballistic animation on fling can be interrupted', (WidgetTester tester) async {
int taps = 0;
await tester.pumpWidget(_boilerplate(() => taps++));
expect(find.text('TapHere'), findsOneWidget);
await tester.tap(find.text('TapHere'));
expect(taps, 1);
expect(find.text('Item 1'), findsOneWidget);
expect(find.text('Item 31'), findsNothing);
expect(find.text('Item 70'), findsNothing);
await tester.fling(find.text('Item 1'), const Offset(0, -200), 2000);
// Don't pump and settle because we want to interrupt the ballistic scrolling animation.
expect(find.text('TapHere'), findsOneWidget);
await tester.tap(find.text('TapHere'), warnIfMissed: false);
expect(taps, 2);
expect(find.text('Item 1'), findsOneWidget);
expect(find.text('Item 31'), findsOneWidget);
expect(find.text('Item 70'), findsNothing);
// Use `dragFrom` here because calling `drag` on a list item without
// first calling `pumpAndSettle` fails with a hit test error.
await tester.dragFrom(const Offset(0, 200), const Offset(0, 200));
await tester.pumpAndSettle();
// Verify that the ballistic animation has canceled and the sheet has
// returned to it's original position.
await tester.tap(find.text('TapHere'));
expect(taps, 3);
expect(find.text('Item 1'), findsOneWidget);
expect(find.text('Item 31'), findsNothing);
expect(find.text('Item 70'), findsNothing);
}, variant: TargetPlatformVariant.all());
debugDefaultTargetPlatformOverride = null;
});