Skip to content

Commit

Permalink
Fix CupertinoSliverRefreshControl onRefresh callback (flutter#32086)
Browse files Browse the repository at this point in the history
Replace CupertinoSliverRefreshControl.onRefresh's then callback with whenCompleted callback, so when onRefresh completes with error the sliver refresh control retracts like when it completes with value.
  • Loading branch information
LongCatIsLooong authored May 4, 2019
1 parent 2f75005 commit 8800153
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 2 deletions.
2 changes: 1 addition & 1 deletion packages/flutter/lib/src/cupertino/refresh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ class _CupertinoSliverRefreshControlState extends State<CupertinoSliverRefreshCo
// user supplied and we're always here in the middle of the sliver's
// performLayout.
SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
refreshTask = widget.onRefresh()..then((_) {
refreshTask = widget.onRefresh()..whenComplete(() {
if (mounted) {
setState(() => refreshTask = null);
// Trigger one more transition because by this time, BoxConstraint's
Expand Down
92 changes: 91 additions & 1 deletion packages/flutter/test/cupertino/refresh_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void main() {
double refreshIndicatorExtent,
) => mockHelper.builder(context, refreshState, pulledExtent, refreshTriggerPullDistance, refreshIndicatorExtent);

final Function onRefresh = () => mockHelper.refreshTask();
Future<void> onRefresh() => mockHelper.refreshTask();

setUp(() {
mockHelper = MockHelper();
Expand All @@ -52,6 +52,7 @@ void main() {
}
return refreshIndicator;
});

when(mockHelper.refreshTask()).thenAnswer((_) => refreshCompleter.future);
});

Expand Down Expand Up @@ -373,6 +374,95 @@ void main() {
},
);

testWidgets(
'refreshing task keeps the sliver expanded forever until completes with error',
(WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
final FlutterError error = FlutterError('Oops');
double errorCount = 0;

runZoned(() async {
refreshCompleter = Completer<void>.sync();

await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
slivers: <Widget>[
CupertinoSliverRefreshControl(
builder: builder,
onRefresh: onRefresh,
),
buildAListOfStuff(),
],
),
),
);

await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0);
await tester.pump();
// Let it start snapping back.
await tester.pump(const Duration(milliseconds: 50));

verifyInOrder(<void>[
mockHelper.builder(
any,
RefreshIndicatorMode.armed,
150.0,
100.0, // Default value.
60.0, // Default value.
),
mockHelper.refreshTask(),
mockHelper.builder(
any,
RefreshIndicatorMode.armed,
argThat(moreOrLessEquals(127.10396988577114)),
100.0, // Default value.
60.0, // Default value.
),
]);

// Reaches refresh state and sliver's at 60.0 in height after a while.
await tester.pump(const Duration(seconds: 1));
verify(mockHelper.builder(
any,
RefreshIndicatorMode.refresh,
60.0,
100.0, // Default value.
60.0, // Default value.
));

// Stays in that state forever until future completes.
await tester.pump(const Duration(seconds: 1000));
verifyNoMoreInteractions(mockHelper);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 60.0),
);

refreshCompleter.completeError(error);
await tester.pump();

verify(mockHelper.builder(
any,
RefreshIndicatorMode.done,
60.0,
100.0, // Default value.
60.0, // Default value.
));
verifyNoMoreInteractions(mockHelper);
},
onError: (dynamic e) {
expect(e, error);
expect(errorCount, 0);
errorCount++;
}
);

debugDefaultTargetPlatformOverride = null;
},
);

testWidgets('expanded refreshing sliver scrolls normally', (WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;

Expand Down

0 comments on commit 8800153

Please sign in to comment.