diff --git a/packages/flutter/lib/src/widgets/overscroll_indicator.dart b/packages/flutter/lib/src/widgets/overscroll_indicator.dart index d9e8c2ea0fd6..d0c1a19198b9 100644 --- a/packages/flutter/lib/src/widgets/overscroll_indicator.dart +++ b/packages/flutter/lib/src/widgets/overscroll_indicator.dart @@ -707,7 +707,15 @@ class _StretchingOverscrollIndicatorState extends State _stretchSize; final Tween _stretchSizeTween = Tween(begin: 0.0, end: 0.0); _StretchState _state = _StretchState.idle; + + double get pullDistance => _pullDistance; double _pullDistance = 0.0; // Constants from Android. @@ -848,7 +858,7 @@ class _StretchController extends ChangeNotifier { /// in the main axis. void pull(double normalizedOverscroll) { assert(normalizedOverscroll >= 0.0); - _pullDistance = normalizedOverscroll + _pullDistance; + _pullDistance = normalizedOverscroll; _stretchSizeTween.begin = _stretchSize.value; final double linearIntensity =_stretchIntensity * _pullDistance; final double exponentialIntensity = _stretchIntensity * (1 - math.exp(-_pullDistance * _exponentialScalar)); diff --git a/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart b/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart index 1fc1efe882d0..e4129e5713a3 100644 --- a/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart +++ b/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart @@ -451,4 +451,125 @@ void main() { await gesture.up(); await tester.pumpAndSettle(); }); + + testWidgets('Stretch limit', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/99264 + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(), + child: ScrollConfiguration( + behavior: const ScrollBehavior().copyWith(overscroll: false), + child: StretchingOverscrollIndicator( + axisDirection: AxisDirection.down, + child: SizedBox( + height: 300, + child: ListView.builder( + itemCount: 20, + itemBuilder: (BuildContext context, int index){ + return Padding( + padding: const EdgeInsets.all(10.0), + child: Text('Index $index'), + ); + }, + ), + ), + ), + ), + ) + ) + ); + const double maxStretchLocation = 52.63178407049861; + + expect(find.text('Index 1'), findsOneWidget); + expect(tester.getCenter(find.text('Index 1')).dy, 51.0); + + TestGesture pointer = await tester.startGesture(tester.getCenter(find.text('Index 1'))); + // Overscroll beyond the limit (the viewport is 600.0). + await pointer.moveBy(const Offset(0.0, 610.0)); + await tester.pumpAndSettle(); + expect(find.text('Index 1'), findsOneWidget); + expect(tester.getCenter(find.text('Index 1')).dy, maxStretchLocation); + + pointer = await tester.startGesture(tester.getCenter(find.text('Index 1'))); + // Overscroll way way beyond the limit + await pointer.moveBy(const Offset(0.0, 1000.0)); + await tester.pumpAndSettle(); + expect(find.text('Index 1'), findsOneWidget); + expect(tester.getCenter(find.text('Index 1')).dy, maxStretchLocation); + + await pointer.up(); + await tester.pumpAndSettle(); + }); + + testWidgets('Multiple pointers wll not exceed stretch limit', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/99264 + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(), + child: ScrollConfiguration( + behavior: const ScrollBehavior().copyWith(overscroll: false), + child: StretchingOverscrollIndicator( + axisDirection: AxisDirection.down, + child: SizedBox( + height: 300, + child: ListView.builder( + itemCount: 20, + itemBuilder: (BuildContext context, int index){ + return Padding( + padding: const EdgeInsets.all(10.0), + child: Text('Index $index'), + ); + }, + ), + ), + ), + ), + ) + ) + ); + expect(find.text('Index 1'), findsOneWidget); + expect(tester.getCenter(find.text('Index 1')).dy, 51.0); + + final TestGesture pointer1 = await tester.startGesture(tester.getCenter(find.text('Index 1'))); + // Overscroll the start. + await pointer1.moveBy(const Offset(0.0, 210.0)); + await tester.pumpAndSettle(); + expect(find.text('Index 1'), findsOneWidget); + double lastStretchedLocation = tester.getCenter(find.text('Index 1')).dy; + expect(lastStretchedLocation, greaterThan(51.0)); + + final TestGesture pointer2 = await tester.startGesture(tester.getCenter(find.text('Index 1'))); + // Add overscroll from an additional pointer + await pointer2.moveBy(const Offset(0.0, 210.0)); + await tester.pumpAndSettle(); + expect(find.text('Index 1'), findsOneWidget); + expect(tester.getCenter(find.text('Index 1')).dy, greaterThan(lastStretchedLocation)); + lastStretchedLocation = tester.getCenter(find.text('Index 1')).dy; + + final TestGesture pointer3 = await tester.startGesture(tester.getCenter(find.text('Index 1'))); + // Add overscroll from an additional pointer, exceeding the max stretch (600) + await pointer3.moveBy(const Offset(0.0, 210.0)); + await tester.pumpAndSettle(); + expect(find.text('Index 1'), findsOneWidget); + expect(tester.getCenter(find.text('Index 1')).dy, greaterThan(lastStretchedLocation)); + lastStretchedLocation = tester.getCenter(find.text('Index 1')).dy; + + final TestGesture pointer4 = await tester.startGesture(tester.getCenter(find.text('Index 1'))); + // Since we have maxed out the overscroll, it should not have stretched + // further, regardless of the number of pointers. + await pointer4.moveBy(const Offset(0.0, 210.0)); + await tester.pumpAndSettle(); + expect(find.text('Index 1'), findsOneWidget); + expect(tester.getCenter(find.text('Index 1')).dy, lastStretchedLocation); + + await pointer1.up(); + await pointer2.up(); + await pointer3.up(); + await pointer4.up(); + await tester.pumpAndSettle(); + }); }