From 691a18df5fd583a63ef726374b2243a8f6b4bb0e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 3 Jun 2024 12:12:11 -0700 Subject: [PATCH] [CupertinoActionSheet] Fix overflow of the overscroll section when the user scrolls far (#149542) When I implemented https://github.com/flutter/flutter/pull/149334, there was a bug that if the actions section is overscrolled for too far (typically due to a fling), the overscroll background might be so long that it overflows the actions section. This PR fixes this bug. --- .../flutter/lib/src/cupertino/dialog.dart | 12 +++++- .../test/cupertino/action_sheet_test.dart | 42 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index 153ceb4e50fee..c49f2a56934ec 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -964,8 +964,16 @@ class _ActionSheetMainSheetState extends State<_ActionSheetMainSheet> { bool _onScrollUpdate(ScrollUpdateNotification notification) { final ScrollMetrics metrics = notification.metrics; setState(() { - _topOverscroll = math.max(metrics.minScrollExtent - metrics.pixels, 0); - _bottomOverscroll = math.max(metrics.pixels - metrics.maxScrollExtent, 0); + // The sizes of the overscroll should not be longer than the height of the + // actions section. + _topOverscroll = math.min( + math.max(metrics.minScrollExtent - metrics.pixels, 0), + metrics.viewportDimension, + ); + _bottomOverscroll = math.min( + math.max(metrics.pixels - metrics.maxScrollExtent, 0), + metrics.viewportDimension, + ); }); return false; } diff --git a/packages/flutter/test/cupertino/action_sheet_test.dart b/packages/flutter/test/cupertino/action_sheet_test.dart index 9f7437288269d..aa7f2d7d9a56d 100644 --- a/packages/flutter/test/cupertino/action_sheet_test.dart +++ b/packages/flutter/test/cupertino/action_sheet_test.dart @@ -489,6 +489,48 @@ void main() { await gesture.up(); }); + testWidgets('Actions section correctly renders overscrolls with very far scrolls', (WidgetTester tester) async { + // When the scroll is really far, the overscroll might be longer than the + // actions section, causing overflow if not controlled. + final ScrollController actionScrollController = ScrollController(); + addTearDown(actionScrollController.dispose); + await tester.pumpWidget( + createAppWithButtonThatLaunchesActionSheet( + Builder(builder: (BuildContext context) { + return CupertinoActionSheet( + message: Text('message' * 300), + actions: List.generate(4, (int i) => + CupertinoActionSheetAction( + onPressed: () {}, + child: Text('Button $i'), + ), + ), + ); + }), + ), + ); + + await tester.tap(find.text('Go')); + await tester.pumpAndSettle(); + + final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('Button 0'))); + await tester.pumpAndSettle(); + await gesture.moveBy(const Offset(0, 40)); // A short drag to start the gesture. + await tester.pumpAndSettle(); + // The drag is far enough to make the overscroll longer than the section. + await gesture.moveBy(const Offset(0, 1000)); + await tester.pump(); + // The buttons should be out of the screen + expect( + tester.getTopLeft(find.text('Button 0')).dy, + greaterThan(tester.getBottomLeft(find.byType(CupertinoActionSheet)).dy) + ); + await expectLater( + find.byType(CupertinoActionSheet), + matchesGoldenFile('cupertinoActionSheet.long-overscroll.0.png'), + ); + }); + testWidgets('Tap on button calls onPressed', (WidgetTester tester) async { bool wasPressed = false; await tester.pumpWidget(