Fix CupertinoSliverRefreshControl onRefresh callback (#32086)
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.
This commit is contained in:
parent
2f75005a16
commit
8800153468
@ -463,7 +463,7 @@ class _CupertinoSliverRefreshControlState extends State<CupertinoSliverRefreshCo
|
|||||||
// user supplied and we're always here in the middle of the sliver's
|
// user supplied and we're always here in the middle of the sliver's
|
||||||
// performLayout.
|
// performLayout.
|
||||||
SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
|
SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
|
||||||
refreshTask = widget.onRefresh()..then((_) {
|
refreshTask = widget.onRefresh()..whenComplete(() {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => refreshTask = null);
|
setState(() => refreshTask = null);
|
||||||
// Trigger one more transition because by this time, BoxConstraint's
|
// Trigger one more transition because by this time, BoxConstraint's
|
||||||
|
@ -29,7 +29,7 @@ void main() {
|
|||||||
double refreshIndicatorExtent,
|
double refreshIndicatorExtent,
|
||||||
) => mockHelper.builder(context, refreshState, pulledExtent, refreshTriggerPullDistance, refreshIndicatorExtent);
|
) => mockHelper.builder(context, refreshState, pulledExtent, refreshTriggerPullDistance, refreshIndicatorExtent);
|
||||||
|
|
||||||
final Function onRefresh = () => mockHelper.refreshTask();
|
Future<void> onRefresh() => mockHelper.refreshTask();
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
mockHelper = MockHelper();
|
mockHelper = MockHelper();
|
||||||
@ -52,6 +52,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
return refreshIndicator;
|
return refreshIndicator;
|
||||||
});
|
});
|
||||||
|
|
||||||
when(mockHelper.refreshTask()).thenAnswer((_) => refreshCompleter.future);
|
when(mockHelper.refreshTask()).thenAnswer((_) => refreshCompleter.future);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -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 {
|
testWidgets('expanded refreshing sliver scrolls normally', (WidgetTester tester) async {
|
||||||
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user