diff --git a/packages/flutter/lib/src/material/refresh_indicator.dart b/packages/flutter/lib/src/material/refresh_indicator.dart index f0dc23e6eb..374f61e653 100644 --- a/packages/flutter/lib/src/material/refresh_indicator.dart +++ b/packages/flutter/lib/src/material/refresh_indicator.dart @@ -226,6 +226,12 @@ class RefreshIndicatorState extends State with TickerProviderS _checkDragOffset(notification.metrics.viewportDimension); } } + if (_mode == _RefreshIndicatorMode.armed && notification.dragDetails == null) { + // On iOS start the refresh when the Scrollable bounces back from the + // overscroll (ScrollNotification indicating this don't have dragDetails + // because the scroll activity is not directly triggered by a drag). + _show(); + } } else if (notification is OverscrollNotification) { if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) { _dragOffset -= notification.overscroll / 2.0; diff --git a/packages/flutter/test/material/refresh_indicator_test.dart b/packages/flutter/test/material/refresh_indicator_test.dart index 689c7d121f..841ef8a96d 100644 --- a/packages/flutter/test/material/refresh_indicator_test.dart +++ b/packages/flutter/test/material/refresh_indicator_test.dart @@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; @@ -378,4 +379,41 @@ void main() { expect(refreshCalled, true); expect(tester.takeException(), isFlutterError); }); + + testWidgets('Refresh starts while scroll view moves back to 0.0 after overscroll on iOS', (WidgetTester tester) async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + refreshCalled = false; + double lastScrollOffset; + final ScrollController controller = new ScrollController(); + + await tester.pumpWidget( + new MaterialApp( + home: new RefreshIndicator( + onRefresh: refresh, + child: new ListView( + controller: controller, + physics: const AlwaysScrollableScrollPhysics(), + children: ['A', 'B', 'C', 'D', 'E', 'F'].map((String item) { + return new SizedBox( + height: 200.0, + child: new Text(item), + ); + }).toList(), + ), + ), + ), + ); + + await tester.fling(find.text('A'), const Offset(0.0, 300.0), 1000.0); + await tester.pump(const Duration(milliseconds: 100)); + expect(lastScrollOffset = controller.offset, lessThan(0.0)); + expect(refreshCalled, isFalse); + + await tester.pump(const Duration(milliseconds: 100)); + expect(controller.offset, greaterThan(lastScrollOffset)); + expect(controller.offset, lessThan(0.0)); + expect(refreshCalled, isTrue); + + debugDefaultTargetPlatformOverride = null; + }); }