diff --git a/packages/flutter/lib/src/material/refresh_indicator.dart b/packages/flutter/lib/src/material/refresh_indicator.dart index ad52bb71380..3f5e4f4839c 100644 --- a/packages/flutter/lib/src/material/refresh_indicator.dart +++ b/packages/flutter/lib/src/material/refresh_indicator.dart @@ -262,7 +262,11 @@ class RefreshIndicatorState extends State with TickerProviderS } bool _shouldStart(ScrollNotification notification) { - return (notification is ScrollStartNotification || (notification is ScrollUpdateNotification && notification.dragDetails != null && widget.triggerMode == RefreshIndicatorTriggerMode.anywhere)) + // If the notification.dragDetails is null, this scroll is not triggered by + // user dragging. It may be a result of ScrollController.jumpTo or ballistic scroll. + // In this case, we don't want to trigger the refresh indicator. + return ((notification is ScrollStartNotification && notification.dragDetails != null) + || (notification is ScrollUpdateNotification && notification.dragDetails != null && widget.triggerMode == RefreshIndicatorTriggerMode.anywhere)) && notification.metrics.extentBefore == 0.0 && _mode == null && _start(notification.metrics.axisDirection); diff --git a/packages/flutter/test/material/refresh_indicator_test.dart b/packages/flutter/test/material/refresh_indicator_test.dart index cdab2bc2657..a8735b3c01d 100644 --- a/packages/flutter/test/material/refresh_indicator_test.dart +++ b/packages/flutter/test/material/refresh_indicator_test.dart @@ -677,6 +677,38 @@ void main() { expect(find.byType(RefreshProgressIndicator), findsNothing); }); + testWidgets('ScrollController.jumpTo should not trigger the refresh indicator', (WidgetTester tester) async { + refreshCalled = false; + final ScrollController scrollController = ScrollController(initialScrollOffset: 500.0); + await tester.pumpWidget( + MaterialApp( + home: RefreshIndicator( + onRefresh: refresh, + child: ListView( + controller: scrollController, + physics: const AlwaysScrollableScrollPhysics(), + children: const [ + SizedBox( + height: 800.0, + child: Text('X'), + ), + SizedBox( + height: 800.0, + child: Text('Y'), + ), + ], + ), + ), + ), + ); + + scrollController.jumpTo(0.0); + await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation + await tester.pump(const Duration(seconds: 1)); // finish the indicator hide animation + + expect(refreshCalled, false); + }); + testWidgets('RefreshIndicator.color can be updated at runtime', (WidgetTester tester) async { refreshCalled = false; Color refreshIndicatorColor = Colors.green;