mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Handle back swipe completed->completed or completed->dismissed transitions (#27866)
This commit is contained in:
parent
7cc694e5d5
commit
8d0346a0ec
|
@ -634,18 +634,22 @@ class _CupertinoBackGestureController<T> {
|
||||||
controller.animateBack(0.0, duration: Duration(milliseconds: droppedPageBackAnimationTime), curve: animationCurve);
|
controller.animateBack(0.0, duration: Duration(milliseconds: droppedPageBackAnimationTime), curve: animationCurve);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(controller.isAnimating);
|
if (controller.isAnimating) {
|
||||||
assert(controller.status != AnimationStatus.completed);
|
// Don't end the gesture until the transition completes.
|
||||||
assert(controller.status != AnimationStatus.dismissed);
|
_animating = true;
|
||||||
|
controller.addStatusListener(_handleStatusChanged);
|
||||||
|
} else {
|
||||||
|
// Animate calls could return inline if already at the target destination
|
||||||
|
// value.
|
||||||
|
return _handleStatusChanged(controller.status);
|
||||||
|
}
|
||||||
|
|
||||||
// Don't end the gesture until the transition completes.
|
|
||||||
_animating = true;
|
|
||||||
controller.addStatusListener(_handleStatusChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleStatusChanged(AnimationStatus status) {
|
void _handleStatusChanged(AnimationStatus status) {
|
||||||
assert(_animating);
|
if (_animating) {
|
||||||
controller.removeStatusListener(_handleStatusChanged);
|
controller.removeStatusListener(_handleStatusChanged);
|
||||||
|
}
|
||||||
_animating = false;
|
_animating = false;
|
||||||
if (status == AnimationStatus.dismissed)
|
if (status == AnimationStatus.dismissed)
|
||||||
navigator.pop<T>(); // this will cause the route to get disposed, which will dispose us
|
navigator.pop<T>(); // this will cause the route to get disposed, which will dispose us
|
||||||
|
|
|
@ -155,7 +155,6 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
|
||||||
overlayEntries.first.opaque = false;
|
overlayEntries.first.opaque = false;
|
||||||
break;
|
break;
|
||||||
case AnimationStatus.dismissed:
|
case AnimationStatus.dismissed:
|
||||||
assert(!overlayEntries.first.opaque);
|
|
||||||
// We might still be the current route if a subclass is controlling the
|
// We might still be the current route if a subclass is controlling the
|
||||||
// the transition and hits the dismissed status. For example, the iOS
|
// the transition and hits the dismissed status. For example, the iOS
|
||||||
// back gesture drives this animation to the dismissed status before
|
// back gesture drives this animation to the dismissed status before
|
||||||
|
|
|
@ -427,6 +427,74 @@ void main() {
|
||||||
expect(find.text('Page 1'), isOnstage);
|
expect(find.text('Page 1'), isOnstage);
|
||||||
expect(find.text('Page 2'), isOnstage);
|
expect(find.text('Page 2'), isOnstage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('test edge swipe then drop back at starting point works', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
CupertinoApp(
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return CupertinoPageRoute<void>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final String pageNumber = settings.name == '/' ? '1' : '2';
|
||||||
|
return Center(child: Text('Page $pageNumber'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), findsNothing);
|
||||||
|
expect(find.text('Page 2'), isOnstage);
|
||||||
|
|
||||||
|
final TestGesture gesture = await tester.startGesture(const Offset(5, 200));
|
||||||
|
await gesture.moveBy(const Offset(300, 0));
|
||||||
|
await tester.pump();
|
||||||
|
// Bring it exactly back such that there's nothing to animate when releasing.
|
||||||
|
await gesture.moveBy(const Offset(-300, 0));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), findsNothing);
|
||||||
|
expect(find.text('Page 2'), isOnstage);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('test edge swipe then drop back at ending point works', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
CupertinoApp(
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return CupertinoPageRoute<void>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final String pageNumber = settings.name == '/' ? '1' : '2';
|
||||||
|
return Center(child: Text('Page $pageNumber'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), findsNothing);
|
||||||
|
expect(find.text('Page 2'), isOnstage);
|
||||||
|
|
||||||
|
final TestGesture gesture = await tester.startGesture(const Offset(5, 200));
|
||||||
|
// The width of the page.
|
||||||
|
await gesture.moveBy(const Offset(800, 0));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), isOnstage);
|
||||||
|
expect(find.text('Page 2'), findsNothing);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class RtlOverrideWidgetsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
|
class RtlOverrideWidgetsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
|
||||||
|
|
|
@ -470,4 +470,74 @@ void main() {
|
||||||
// An exception should've been thrown because the `builder` returned null.
|
// An exception should've been thrown because the `builder` returned null.
|
||||||
expect(tester.takeException(), isInstanceOf<FlutterError>());
|
expect(tester.takeException(), isInstanceOf<FlutterError>());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('test iOS edge swipe then drop back at starting point works', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(platform: TargetPlatform.iOS),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final String pageNumber = settings.name == '/' ? '1' : '2';
|
||||||
|
return Center(child: Text('Page $pageNumber'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), findsNothing);
|
||||||
|
expect(find.text('Page 2'), isOnstage);
|
||||||
|
|
||||||
|
final TestGesture gesture = await tester.startGesture(const Offset(5, 200));
|
||||||
|
await gesture.moveBy(const Offset(300, 0));
|
||||||
|
await tester.pump();
|
||||||
|
// Bring it exactly back such that there's nothing to animate when releasing.
|
||||||
|
await gesture.moveBy(const Offset(-300, 0));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), findsNothing);
|
||||||
|
expect(find.text('Page 2'), isOnstage);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('test iOS edge swipe then drop back at ending point works', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(platform: TargetPlatform.iOS),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final String pageNumber = settings.name == '/' ? '1' : '2';
|
||||||
|
return Center(child: Text('Page $pageNumber'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), findsNothing);
|
||||||
|
expect(find.text('Page 2'), isOnstage);
|
||||||
|
|
||||||
|
final TestGesture gesture = await tester.startGesture(const Offset(5, 200));
|
||||||
|
// The width of the page.
|
||||||
|
await gesture.moveBy(const Offset(800, 0));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Page 1'), isOnstage);
|
||||||
|
expect(find.text('Page 2'), findsNothing);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue