-
Notifications
You must be signed in to change notification settings - Fork 28.9k
fix cupertino page route dismisses hero transition when swipe to the … #58024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -633,7 +636,8 @@ class _CupertinoBackGestureController<T> { | |||
/// The drag gesture has changed by [fractionalDelta]. The total range of the | |||
/// drag should be 0.0 to 1.0. | |||
void dragUpdate(double delta) { | |||
controller.value -= delta; | |||
final double newValue = controller.value - delta; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this solution. Ideally the animation should be smart enough to not send animation status update when there is an ongoing gesture. However, I can't think of a good way to do it because the animation is provided from TransitionRoute and we also need to send animation status update when the gesture is lifted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the clamp prevents the animation from finishing by keeping its value between 0.0 and 1.0? I can't think of a better approach off the top of my head...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just some nits.
@@ -37,6 +37,9 @@ const Color _kModalBarrierColor = CupertinoDynamicColor.withBrightness( | |||
// The duration of the transition used when a modal popup is shown. | |||
const Duration _kModalPopupTransitionDuration = Duration(milliseconds: 335); | |||
|
|||
const double _kdragingAnimationUpperLimit = 0.998; | |||
const double _kdragingAnimationLowerLimit = 0.002; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The letter after _k should be capitalized for these two, and dragging should have two letter g's.
Also maybe add a comment about where these values came from, like whether they're just what felt right to you, or if they were calculated somehow, etc.
await tester.pump(); | ||
await tester.pump(const Duration(milliseconds: 50)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could pumpAndSettle
work here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for two other cases below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to make sure the ballistic scroll is small enough that can be neglect by human eyes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, sounds good.
@@ -633,7 +636,8 @@ class _CupertinoBackGestureController<T> { | |||
/// The drag gesture has changed by [fractionalDelta]. The total range of the | |||
/// drag should be 0.0 to 1.0. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment about 0.0-1.0 still accurate with the changes?
@@ -633,7 +636,8 @@ class _CupertinoBackGestureController<T> { | |||
/// The drag gesture has changed by [fractionalDelta]. The total range of the | |||
/// drag should be 0.0 to 1.0. | |||
void dragUpdate(double delta) { | |||
controller.value -= delta; | |||
final double newValue = controller.value - delta; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the clamp prevents the animation from finishing by keeping its value between 0.0 and 1.0? I can't think of a better approach off the top of my head...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's triggering the removal of the hero overlay? Is the HorizontalDragGestureRecognizer calling onEnd somehow?
await tester.pump(); | ||
await tester.pump(const Duration(milliseconds: 50)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this causes the side effect that there will always be a ballistic scroll at the end of a drag no matter where we dropped it off?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I try to update the test to make sure it just requires a small pump to finish the ballistic scroll that can be neglected by human eyes
@@ -1133,6 +1133,76 @@ void main() { | |||
expect(nestedObserver.popupCount, 0); | |||
}); | |||
|
|||
testWidgets('showCupertinoModalPopup uses root navigator by default', (WidgetTester tester) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this description right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no its not, thanks for catching it
It trigger removal when the animation status become dismissed or completed. The animation of the route is AnimationController, that it will update its status to dismissed if its value reach 0 or completed if reach 1.0
|
ah ya, that's unfortunate. Looks like native iOS allows for the page to be fully swiped to the edge then back too. Changing the whole animation controller behavior is probably too big of a change. What about making use of navigator didStartUserGesture and somehow not hooking the animation status listener until the gesture stopped? |
thank you. I think i found a better solution and avoid the breaking change. @justinmc @xster ready for review |
// This also relies on the animation to update its status at the end of the | ||
// gesture. See the _CupertinoBackGestureController.dragEnd for how | ||
// cupertino page route achieves that. | ||
if (manifest.fromRoute?.navigator?.userGestureInProgress == true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing that troubles me in this approach is that we push the responsibility, that gesture should trigger animation update when it ends, to the route transition class. I am worrying the third party library developer might trip on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we already crossed that bridge. Since Navigator and Hero are both from the widgets package and heroes.dart is already making use of the user gesture state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this still work if the user dragged it to the end and dropped it exactly there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes we have tests to ensure that
testWidgets('test edge swipe then drop back at ending point works', (WidgetTester tester) async { |
testWidgets('test edge swipe then drop back at starting point works', (WidgetTester tester) async { |
@justinmc the pr has changed a lot since your last review, can you take another look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just two nits.
@@ -533,6 +533,14 @@ class _HeroFlight { | |||
} | |||
|
|||
void _handleAnimationUpdate(AnimationStatus status) { | |||
// The animation will not finish until the user lift their finger, so we |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// The animation will not finish until the user lift their finger, so we | |
// The animation will not finish until the user lifts their finger, so we |
box = tester.renderObject(find.byKey(container)) as RenderBox; | ||
final double firstPosition = box.localToGlobal(Offset.zero).dx; | ||
// Checks the hero is in-transit. | ||
expect(finalPosition > firstPosition, isTrue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Same thing, but maybe the error message is more informative if you do this:
expect(finalPosition > firstPosition, isTrue); | |
expect(finalPosition, greaterThan(firstPosition)); |
flutter#58024) * fix cupertino page route dismisses hero transition when swipe to the edge * add more comment * addressing comments
…edge
Description
When doing cupertino back swipe, the page animation will be dismissed/completed when the gesture reach either end of the screen. This will cause the hero controller to remove the overlay entry of the flighting hero. If the user keep holding the finger and swipe, the hero animation will not respond because it is no longer in the overlay
Related Issues
Fixes #57581
Tests
I added the following tests:
See files
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze --flutter-repo
) does not report any problems on my PR.Breaking Change
Did any tests fail when you ran them? Please read Handling breaking changes.