diff --git a/packages/flutter/lib/src/material/refresh_indicator.dart b/packages/flutter/lib/src/material/refresh_indicator.dart index 0fed2b4784..90719320a9 100644 --- a/packages/flutter/lib/src/material/refresh_indicator.dart +++ b/packages/flutter/lib/src/material/refresh_indicator.dart @@ -372,17 +372,12 @@ class RefreshIndicatorState extends State with TickerProviderS } } else if (notification is ScrollUpdateNotification) { if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) { - if ((notification.metrics.axisDirection == AxisDirection.down && notification.metrics.extentBefore > 0.0) - || (notification.metrics.axisDirection == AxisDirection.up && notification.metrics.extentAfter > 0.0)) { - _dismiss(_RefreshIndicatorMode.canceled); - } else { - if (notification.metrics.axisDirection == AxisDirection.down) { - _dragOffset = _dragOffset! - notification.scrollDelta!; - } else if (notification.metrics.axisDirection == AxisDirection.up) { - _dragOffset = _dragOffset! + notification.scrollDelta!; - } - _checkDragOffset(notification.metrics.viewportDimension); + if (notification.metrics.axisDirection == AxisDirection.down) { + _dragOffset = _dragOffset! - notification.scrollDelta!; + } else if (notification.metrics.axisDirection == AxisDirection.up) { + _dragOffset = _dragOffset! + notification.scrollDelta!; } + _checkDragOffset(notification.metrics.viewportDimension); } if (_mode == _RefreshIndicatorMode.armed && notification.dragDetails == null) { // On iOS start the refresh when the Scrollable bounces back from the @@ -402,7 +397,11 @@ class RefreshIndicatorState extends State with TickerProviderS } else if (notification is ScrollEndNotification) { switch (_mode) { case _RefreshIndicatorMode.armed: - _show(); + if (_positionController.value < 1.0) { + _dismiss(_RefreshIndicatorMode.canceled); + } else { + _show(); + } case _RefreshIndicatorMode.drag: _dismiss(_RefreshIndicatorMode.canceled); case _RefreshIndicatorMode.canceled: diff --git a/packages/flutter/test/material/refresh_indicator_test.dart b/packages/flutter/test/material/refresh_indicator_test.dart index 68e687a2ce..1e4f3dbe11 100644 --- a/packages/flutter/test/material/refresh_indicator_test.dart +++ b/packages/flutter/test/material/refresh_indicator_test.dart @@ -252,7 +252,7 @@ void main() { ), ); - await tester.fling(find.text('X'), const Offset(0.0, 100.0), 1000.0); + await tester.fling(find.text('X'), const Offset(0.0, 200.0), 1000.0); await tester.pump(); await tester.pump(const Duration(seconds: 1)); await tester.pump(const Duration(seconds: 1)); @@ -260,6 +260,76 @@ void main() { expect(refreshCalled, true); }); + testWidgets('RefreshIndicator - drag back not far enough to cancel', (WidgetTester tester) async { + refreshCalled = false; + await tester.pumpWidget( + MaterialApp( + home: RefreshIndicator( + onRefresh: refresh, + child: ListView( + physics: const AlwaysScrollableScrollPhysics(), + children: const [ + SizedBox( + height: 200.0, + child: Text('X'), + ), + SizedBox(height: 1000), + ], + ), + ), + ), + ); + + final Offset startLocation = tester.getCenter(find.text('X'), warnIfMissed: true, callee: 'drag'); + final TestPointer testPointer = TestPointer(); + await tester.sendEventToBinding(testPointer.down(startLocation)); + await tester.sendEventToBinding(testPointer.move(startLocation + const Offset(0.0, 175))); + await tester.pump(); + await tester.sendEventToBinding(testPointer.move(startLocation + const Offset(0.0, 150))); + await tester.pump(); + await tester.sendEventToBinding(testPointer.up()); + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); + await tester.pump(const Duration(seconds: 1)); + await tester.pump(const Duration(seconds: 1)); + expect(refreshCalled, true); + }); + + testWidgets('RefreshIndicator - drag back far enough to cancel', (WidgetTester tester) async { + refreshCalled = false; + await tester.pumpWidget( + MaterialApp( + home: RefreshIndicator( + onRefresh: refresh, + child: ListView( + physics: const AlwaysScrollableScrollPhysics(), + children: const [ + SizedBox( + height: 200.0, + child: Text('X'), + ), + SizedBox(height: 1000), + ], + ), + ), + ), + ); + + final Offset startLocation = tester.getCenter(find.text('X'), warnIfMissed: true, callee: 'drag'); + final TestPointer testPointer = TestPointer(); + await tester.sendEventToBinding(testPointer.down(startLocation)); + await tester.sendEventToBinding(testPointer.move(startLocation + const Offset(0.0, 175))); + await tester.pump(); + await tester.sendEventToBinding(testPointer.move(startLocation + const Offset(0.0, 149))); + await tester.pump(); + await tester.sendEventToBinding(testPointer.up()); + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); + await tester.pump(const Duration(seconds: 1)); + await tester.pump(const Duration(seconds: 1)); + expect(refreshCalled, false); + }); + testWidgets('RefreshIndicator - show - slow', (WidgetTester tester) async { refreshCalled = false; await tester.pumpWidget(