Unverified Commit 14a406f8 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Show `RefreshIndicator` on top when scroll's axis direction is up (matching...

Show `RefreshIndicator` on top when scroll's axis direction is up (matching native behaviour) (#93779)
parent 9fa42753
...@@ -296,7 +296,8 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS ...@@ -296,7 +296,8 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
// In this case, we don't want to trigger the refresh indicator. // In this case, we don't want to trigger the refresh indicator.
return ((notification is ScrollStartNotification && notification.dragDetails != null) return ((notification is ScrollStartNotification && notification.dragDetails != null)
|| (notification is ScrollUpdateNotification && notification.dragDetails != null && widget.triggerMode == RefreshIndicatorTriggerMode.anywhere)) || (notification is ScrollUpdateNotification && notification.dragDetails != null && widget.triggerMode == RefreshIndicatorTriggerMode.anywhere))
&& notification.metrics.extentBefore == 0.0 && (( notification.metrics.axisDirection == AxisDirection.up && notification.metrics.extentAfter == 0.0)
|| (notification.metrics.axisDirection == AxisDirection.down && notification.metrics.extentBefore == 0.0))
&& _mode == null && _mode == null
&& _start(notification.metrics.axisDirection); && _start(notification.metrics.axisDirection);
} }
...@@ -313,10 +314,8 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS ...@@ -313,10 +314,8 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
bool? indicatorAtTopNow; bool? indicatorAtTopNow;
switch (notification.metrics.axisDirection) { switch (notification.metrics.axisDirection) {
case AxisDirection.down: case AxisDirection.down:
indicatorAtTopNow = true;
break;
case AxisDirection.up: case AxisDirection.up:
indicatorAtTopNow = false; indicatorAtTopNow = true;
break; break;
case AxisDirection.left: case AxisDirection.left:
case AxisDirection.right: case AxisDirection.right:
...@@ -328,10 +327,15 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS ...@@ -328,10 +327,15 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
_dismiss(_RefreshIndicatorMode.canceled); _dismiss(_RefreshIndicatorMode.canceled);
} else if (notification is ScrollUpdateNotification) { } else if (notification is ScrollUpdateNotification) {
if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) { if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) {
if (notification.metrics.extentBefore > 0.0) { if ((notification.metrics.axisDirection == AxisDirection.down && notification.metrics.extentBefore > 0.0)
|| (notification.metrics.axisDirection == AxisDirection.up && notification.metrics.extentAfter > 0.0)) {
_dismiss(_RefreshIndicatorMode.canceled); _dismiss(_RefreshIndicatorMode.canceled);
} else { } else {
_dragOffset = _dragOffset! - notification.scrollDelta!; 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); _checkDragOffset(notification.metrics.viewportDimension);
} }
} }
...@@ -343,7 +347,11 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS ...@@ -343,7 +347,11 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
} }
} else if (notification is OverscrollNotification) { } else if (notification is OverscrollNotification) {
if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) { if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) {
_dragOffset = _dragOffset! - notification.overscroll; if (notification.metrics.axisDirection == AxisDirection.down) {
_dragOffset = _dragOffset! - notification.overscroll;
} else if (notification.metrics.axisDirection == AxisDirection.up) {
_dragOffset = _dragOffset! + notification.overscroll;
}
_checkDragOffset(notification.metrics.viewportDimension); _checkDragOffset(notification.metrics.viewportDimension);
} }
} else if (notification is ScrollEndNotification) { } else if (notification is ScrollEndNotification) {
...@@ -382,10 +390,8 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS ...@@ -382,10 +390,8 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
assert(_dragOffset == null); assert(_dragOffset == null);
switch (direction) { switch (direction) {
case AxisDirection.down: case AxisDirection.down:
_isIndicatorAtTop = true;
break;
case AxisDirection.up: case AxisDirection.up:
_isIndicatorAtTop = false; _isIndicatorAtTop = true;
break; break;
case AxisDirection.left: case AxisDirection.left:
case AxisDirection.right: case AxisDirection.right:
......
...@@ -95,7 +95,7 @@ void main() { ...@@ -95,7 +95,7 @@ void main() {
expect(refreshCalled, true); expect(refreshCalled, true);
}); });
testWidgets('RefreshIndicator - bottom', (WidgetTester tester) async { testWidgets('RefreshIndicator - reverse', (WidgetTester tester) async {
refreshCalled = false; refreshCalled = false;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -115,7 +115,7 @@ void main() { ...@@ -115,7 +115,7 @@ void main() {
), ),
); );
await tester.fling(find.text('X'), const Offset(0.0, -300.0), 1000.0); await tester.fling(find.text('X'), const Offset(0.0, 600.0), 1000.0);
await tester.pump(); await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
...@@ -149,7 +149,7 @@ void main() { ...@@ -149,7 +149,7 @@ void main() {
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0)); expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
}); });
testWidgets('RefreshIndicator - bottom - position', (WidgetTester tester) async { testWidgets('RefreshIndicator - reverse - position', (WidgetTester tester) async {
refreshCalled = false; refreshCalled = false;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -169,11 +169,11 @@ void main() { ...@@ -169,11 +169,11 @@ void main() {
), ),
); );
await tester.fling(find.text('X'), const Offset(0.0, -300.0), 1000.0); await tester.fling(find.text('X'), const Offset(0.0, 600.0), 1000.0);
await tester.pump(); await tester.pump();
await tester.pump(const Duration(seconds: 1)); await tester.pump(const Duration(seconds: 1));
await tester.pump(const Duration(seconds: 1)); await tester.pump(const Duration(seconds: 1));
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, greaterThan(300.0)); expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
}); });
testWidgets('RefreshIndicator - no movement', (WidgetTester tester) async { testWidgets('RefreshIndicator - no movement', (WidgetTester tester) async {
...@@ -612,7 +612,7 @@ void main() { ...@@ -612,7 +612,7 @@ void main() {
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0)); expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
}); });
testWidgets('Bottom RefreshIndicator(anywhere mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async { testWidgets('Reverse RefreshIndicator(anywhere mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
refreshCalled = false; refreshCalled = false;
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -641,11 +641,11 @@ void main() { ...@@ -641,11 +641,11 @@ void main() {
scrollController.jumpTo(50.0); scrollController.jumpTo(50.0);
await tester.fling(find.text('X'), const Offset(0.0, -300.0), 1000.0); await tester.fling(find.text('X'), const Offset(0.0, 600.0), 1000.0);
await tester.pump(); await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, greaterThan(300.0)); expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
}); });
// Regression test for https://github.com/flutter/flutter/issues/71936 // Regression test for https://github.com/flutter/flutter/issues/71936
...@@ -719,7 +719,7 @@ void main() { ...@@ -719,7 +719,7 @@ void main() {
expect(find.byType(RefreshProgressIndicator), findsNothing); expect(find.byType(RefreshProgressIndicator), findsNothing);
}); });
testWidgets('Bottom RefreshIndicator(onEdge mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async { testWidgets('Reverse RefreshIndicator(onEdge mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
refreshCalled = false; refreshCalled = false;
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -817,7 +817,7 @@ void main() { ...@@ -817,7 +817,7 @@ void main() {
), ),
); );
await tester.fling(find.text('X'), const Offset(0.0, -300.0), 1000.0); await tester.fling(find.text('X'), const Offset(0.0, 600.0), 1000.0);
await tester.pump(); await tester.pump();
expect(tester.widget<RefreshProgressIndicator>(find.byType(RefreshProgressIndicator)).valueColor!.value, primaryColor); expect(tester.widget<RefreshProgressIndicator>(find.byType(RefreshProgressIndicator)).valueColor!.value, primaryColor);
}); });
...@@ -857,7 +857,7 @@ void main() { ...@@ -857,7 +857,7 @@ void main() {
), ),
); );
await tester.fling(find.text('X'), const Offset(0.0, -300.0), 1000.0); await tester.fling(find.text('X'), const Offset(0.0, 600.0), 1000.0);
await tester.pump(); await tester.pump();
expect(tester.widget<RefreshProgressIndicator>(find.byType(RefreshProgressIndicator)).valueColor!.value, refreshIndicatorColor.withOpacity(1.0)); expect(tester.widget<RefreshProgressIndicator>(find.byType(RefreshProgressIndicator)).valueColor!.value, refreshIndicatorColor.withOpacity(1.0));
...@@ -868,4 +868,38 @@ void main() { ...@@ -868,4 +868,38 @@ void main() {
await tester.pump(); await tester.pump();
expect(tester.widget<RefreshProgressIndicator>(find.byType(RefreshProgressIndicator)).valueColor!.value, red.withOpacity(1.0)); expect(tester.widget<RefreshProgressIndicator>(find.byType(RefreshProgressIndicator)).valueColor!.value, red.withOpacity(1.0));
}); });
testWidgets('RefreshIndicator - reverse - BouncingScrollPhysics', (WidgetTester tester) async {
refreshCalled = false;
await tester.pumpWidget(
MaterialApp(
home: RefreshIndicator(
onRefresh: refresh,
child: ListView(
reverse: true,
physics: const BouncingScrollPhysics(),
children: <Widget>[
for (int i = 0; i < 4; i++)
SizedBox(
height: 200.0,
child: Text('X - $i'),
),
],
)
),
),
);
// Scroll to top
await tester.fling(find.text('X - 0'), const Offset(0.0, 800.0), 1000.0);
await tester.pumpAndSettle();
// Fling down to show refresh indicator
await tester.fling(find.text('X - 3'), const Offset(0.0, 250.0), 1000.0);
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
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, true);
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment