diff --git a/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart b/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart index ec0451633fd..5bbe87d515a 100644 --- a/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart +++ b/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart @@ -490,6 +490,8 @@ class _DraggableScrollableSheetScrollPosition velocity = ballisticController.velocity + (physics.tolerance.velocity * ballisticController.velocity.sign); super.goBallistic(velocity); ballisticController.stop(); + } else if (ballisticController.isCompleted) { + super.goBallistic(0); } } diff --git a/packages/flutter/test/widgets/draggable_scrollable_sheet_test.dart b/packages/flutter/test/widgets/draggable_scrollable_sheet_test.dart index 00ec06ed1ec..2c990af943c 100644 --- a/packages/flutter/test/widgets/draggable_scrollable_sheet_test.dart +++ b/packages/flutter/test/widgets/draggable_scrollable_sheet_test.dart @@ -15,6 +15,7 @@ void main() { double minChildSize = .25, double itemExtent, Key containerKey, + NotificationListenerCallback onScrollNotification, }) { return Directionality( textDirection: TextDirection.ltr, @@ -29,14 +30,17 @@ void main() { minChildSize: minChildSize, initialChildSize: initialChildSize, builder: (BuildContext context, ScrollController scrollController) { - return Container( - key: containerKey, - color: const Color(0xFFABCDEF), - child: ListView.builder( - controller: scrollController, - itemExtent: itemExtent, - itemCount: itemCount, - itemBuilder: (BuildContext context, int index) => Text('Item $index'), + return NotificationListener( + onNotification: onScrollNotification, + child: Container( + key: containerKey, + color: const Color(0xFFABCDEF), + child: ListView.builder( + controller: scrollController, + itemExtent: itemExtent, + itemCount: itemCount, + itemBuilder: (BuildContext context, int index) => Text('Item $index'), + ), ), ); }, @@ -260,5 +264,49 @@ void main() { debugDefaultTargetPlatformOverride = null; }); + + testWidgets('ScrollNotification correctly dispatched when flung without covering its container', (WidgetTester tester) async { + final List notificationTypes = []; + await tester.pumpWidget(_boilerplate( + null, + onScrollNotification: (ScrollNotification notification) { + notificationTypes.add(notification.runtimeType); + return false; + }, + )); + + await tester.fling(find.text('Item 1'), const Offset(0, -200), 200); + await tester.pumpAndSettle(); + + // TODO(itome): Make sure UserScrollNotification and ScrollUpdateNotification are called correctly. + final List types = [ + ScrollStartNotification, + ScrollEndNotification, + ]; + expect(notificationTypes, equals(types)); + }); + + testWidgets('ScrollNotification correctly dispatched when flung with contents scroll', (WidgetTester tester) async { + final List notificationTypes = []; + await tester.pumpWidget(_boilerplate( + null, + onScrollNotification: (ScrollNotification notification) { + notificationTypes.add(notification.runtimeType); + return false; + }, + )); + + await tester.flingFrom(const Offset(0, 325), const Offset(0, -325), 200); + await tester.pumpAndSettle(); + + final List types = [ + ScrollStartNotification, + UserScrollNotification, + ...List.filled(5, ScrollUpdateNotification), + ScrollEndNotification, + UserScrollNotification, + ]; + expect(notificationTypes, types); + }); } }