Unverified Commit 7bf9aea2 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Fix user gesture in CupertinoPageRoute (#39590)

parent d8ca42e2
...@@ -731,6 +731,33 @@ class HeroController extends NavigatorObserver { ...@@ -731,6 +731,33 @@ class HeroController extends NavigatorObserver {
_maybeStartHeroTransition(route, previousRoute, HeroFlightDirection.pop, true); _maybeStartHeroTransition(route, previousRoute, HeroFlightDirection.pop, true);
} }
@override
void didStopUserGesture() {
if (navigator.userGestureInProgress)
return;
// If the user horizontal drag gesture initiated the flight (i.e. the back swipe)
// didn't move towards the pop direction at all, the animation will not play
// and thus the status update callback _handleAnimationUpdate will never be
// called when the gesture finishes. In this case the initiated flight needs
// to be manually invalidated.
bool isInvalidFlight(_HeroFlight flight) {
return flight.manifest.isUserGestureTransition
&& flight.manifest.type == HeroFlightDirection.pop
&& flight._proxyAnimation.isDismissed;
}
final List<_HeroFlight> invalidFlights = _flights.values
.where(isInvalidFlight)
.toList(growable: false);
// Treat these invalidated flights as dismissed. Calling _handleAnimationUpdate
// will also remove the flight from _flights.
for (_HeroFlight flight in invalidFlights) {
flight._handleAnimationUpdate(AnimationStatus.dismissed);
}
}
// If we're transitioning between different page routes, start a hero transition // If we're transitioning between different page routes, start a hero transition
// after the toRoute has been laid out with its animation's value at 1.0. // after the toRoute has been laid out with its animation's value at 1.0.
void _maybeStartHeroTransition( void _maybeStartHeroTransition(
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/gestures.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
...@@ -25,6 +26,7 @@ Future<ui.Image> createTestImage() { ...@@ -25,6 +26,7 @@ Future<ui.Image> createTestImage() {
Key firstKey = const Key('first'); Key firstKey = const Key('first');
Key secondKey = const Key('second'); Key secondKey = const Key('second');
Key thirdKey = const Key('third'); Key thirdKey = const Key('third');
Key simpleKey = const Key('simple');
Key homeRouteKey = const Key('homeRoute'); Key homeRouteKey = const Key('homeRoute');
Key routeTwoKey = const Key('routeTwo'); Key routeTwoKey = const Key('routeTwo');
...@@ -52,6 +54,10 @@ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ ...@@ -52,6 +54,10 @@ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
child: const Text('twoInset'), child: const Text('twoInset'),
onPressed: () { Navigator.pushNamed(context, '/twoInset'); }, onPressed: () { Navigator.pushNamed(context, '/twoInset'); },
), ),
FlatButton(
child: const Text('simple'),
onPressed: () { Navigator.pushNamed(context, '/simple'); },
),
], ],
), ),
), ),
...@@ -108,6 +114,19 @@ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ ...@@ -108,6 +114,19 @@ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
], ],
), ),
), ),
// This route is the same as /two except that Hero 'a' is shifted to the right by
// 50 pixels. When the hero's in-flight bounds between / and /twoInset are animated
// using MaterialRectArcTween (the default) they'll follow a different path
// then when the flight starts at /twoInset and returns to /.
'/simple': (BuildContext context) => CupertinoPageScaffold(
child: Center(
child: Hero(
tag: 'a',
transitionOnUserGestures: transitionFromUserGestures,
child: Container(height: 150.0, width: 150.0, key: simpleKey),
),
),
),
}; };
class ThreeRoute extends MaterialPageRoute<void> { class ThreeRoute extends MaterialPageRoute<void> {
...@@ -2257,6 +2276,38 @@ Future<void> main() async { ...@@ -2257,6 +2276,38 @@ Future<void> main() async {
}, },
); );
// Regression test for https://github.com/flutter/flutter/issues/38183.
testWidgets('Remove user gesture driven flights when the gesture is invalid', (WidgetTester tester) async {
transitionFromUserGestures = true;
await tester.pumpWidget(MaterialApp(
theme: ThemeData(
platform: TargetPlatform.iOS,
),
routes: routes,
));
await tester.tap(find.text('simple'));
await tester.pump();
await tester.pumpAndSettle();
expect(find.byKey(simpleKey), findsOneWidget);
// Tap once to trigger a flight.
await tester.tapAt(const Offset(10, 200));
await tester.pumpAndSettle();
// Wait till the previous gesture is accepted.
await tester.pump(const Duration(milliseconds: 500));
// Tap again to trigger another flight, see if it throws.
await tester.tapAt(const Offset(10, 200));
await tester.pumpAndSettle();
// The simple route should still be on top.
expect(find.byKey(simpleKey), findsOneWidget);
expect(tester.takeException(), isNull);
});
// Regression test for https://github.com/flutter/flutter/issues/40239. // Regression test for https://github.com/flutter/flutter/issues/40239.
testWidgets( testWidgets(
'In a pop transition, when fromHero is null, the to hero should eventually become visible', 'In a pop transition, when fromHero is null, the to hero should eventually become visible',
......
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