mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
fix a bug when android uses CupertinoPageTransitionsBuilder... (#114303)
When android uses iOS style `PageTransitionsBuilder` and iOS uses android style `PageTransitionsBuilder`, on android, swipe from the left edge of the screen doesn't work. This PR solves that problem. #99919 introduced a breaking change, the pr re-implemented it <del>without introducing a breaking change.**</del>
This commit is contained in:
parent
168d807734
commit
aaaea51dd2
@ -80,6 +80,8 @@ class MaterialPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixi
|
||||
/// * [CupertinoPageTransitionsBuilder], which is the default page transition
|
||||
/// for iOS and macOS.
|
||||
mixin MaterialRouteTransitionMixin<T> on PageRoute<T> {
|
||||
TargetPlatform? _effectiveTargetPlatform;
|
||||
|
||||
/// Builds the primary contents of the route.
|
||||
@protected
|
||||
Widget buildContent(BuildContext context);
|
||||
@ -116,8 +118,20 @@ mixin MaterialRouteTransitionMixin<T> on PageRoute<T> {
|
||||
|
||||
@override
|
||||
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
|
||||
return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: navigator!.userGestureInProgressNotifier,
|
||||
builder: (BuildContext context, bool useGestureInProgress, Widget? _) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
|
||||
if (useGestureInProgress) {
|
||||
// The platform should be kept unchanged during an user gesture.
|
||||
_effectiveTargetPlatform ??= themeData.platform;
|
||||
} else {
|
||||
_effectiveTargetPlatform = themeData.platform;
|
||||
}
|
||||
return themeData.pageTransitionsTheme.buildTransitions<T>(this, context, animation, secondaryAnimation, child, _effectiveTargetPlatform!);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -741,7 +741,7 @@ class PageTransitionsTheme with Diagnosticable {
|
||||
Map<TargetPlatform, PageTransitionsBuilder> get builders => _builders;
|
||||
final Map<TargetPlatform, PageTransitionsBuilder> _builders;
|
||||
|
||||
/// Delegates to the builder for the current [ThemeData.platform].
|
||||
/// Delegates to the builder for the current [platform].
|
||||
/// If a builder for the current platform is not found, then the
|
||||
/// [ZoomPageTransitionsBuilder] is used.
|
||||
///
|
||||
@ -752,13 +752,8 @@ class PageTransitionsTheme with Diagnosticable {
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
TargetPlatform platform,
|
||||
) {
|
||||
TargetPlatform platform = Theme.of(context).platform;
|
||||
|
||||
if (CupertinoRouteTransitionMixin.isPopGestureInProgress(route)) {
|
||||
platform = TargetPlatform.iOS;
|
||||
}
|
||||
|
||||
final PageTransitionsBuilder matchingBuilder =
|
||||
builders[platform] ?? const ZoomPageTransitionsBuilder();
|
||||
return matchingBuilder.buildTransitions<T>(route, context, animation, secondaryAnimation, child);
|
||||
|
||||
@ -350,4 +350,140 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
expect(builtCount, 1);
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
||||
|
||||
testWidgets('android can use CupertinoPageTransitionsBuilder', (WidgetTester tester) async {
|
||||
int builtCount = 0;
|
||||
|
||||
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
|
||||
'/': (BuildContext context) => Material(
|
||||
child: TextButton(
|
||||
child: const Text('push'),
|
||||
onPressed: () { Navigator.of(context).pushNamed('/b'); },
|
||||
),
|
||||
),
|
||||
'/b': (BuildContext context) => StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
builtCount++;
|
||||
return TextButton(
|
||||
child: const Text('pop'),
|
||||
onPressed: () { Navigator.pop(context); },
|
||||
);
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
pageTransitionsTheme: const PageTransitionsTheme(
|
||||
builders: <TargetPlatform, PageTransitionsBuilder>{
|
||||
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
||||
// iOS uses different PageTransitionsBuilder
|
||||
TargetPlatform.iOS: FadeUpwardsPageTransitionsBuilder(),
|
||||
},
|
||||
),
|
||||
),
|
||||
routes: routes,
|
||||
),
|
||||
);
|
||||
|
||||
// No matter push or pop was called, the child widget should built only once.
|
||||
await tester.tap(find.text('push'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(builtCount, 1);
|
||||
|
||||
final Size size = tester.getSize(find.byType(MaterialApp));
|
||||
await tester.flingFrom(Offset(0, size.height / 2), Offset(size.width * 2 / 3, 0), 500);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('push'), findsOneWidget);
|
||||
expect(builtCount, 1);
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
||||
|
||||
testWidgets('back gesture while TargetPlatform changes', (WidgetTester tester) async {
|
||||
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
|
||||
'/': (BuildContext context) => Material(
|
||||
child: TextButton(
|
||||
child: const Text('PUSH'),
|
||||
onPressed: () { Navigator.of(context).pushNamed('/b'); },
|
||||
),
|
||||
),
|
||||
'/b': (BuildContext context) => const Text('HELLO'),
|
||||
};
|
||||
const PageTransitionsTheme pageTransitionsTheme = PageTransitionsTheme(
|
||||
builders: <TargetPlatform, PageTransitionsBuilder>{
|
||||
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
||||
// iOS uses different PageTransitionsBuilder
|
||||
TargetPlatform.iOS: FadeUpwardsPageTransitionsBuilder(),
|
||||
},
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
platform: TargetPlatform.android,
|
||||
pageTransitionsTheme: pageTransitionsTheme,
|
||||
),
|
||||
routes: routes,
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('PUSH'));
|
||||
expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 2);
|
||||
expect(find.text('PUSH'), findsNothing);
|
||||
expect(find.text('HELLO'), findsOneWidget);
|
||||
|
||||
final Offset helloPosition1 = tester.getCenter(find.text('HELLO'));
|
||||
final TestGesture gesture = await tester.startGesture(const Offset(2.5, 300.0));
|
||||
await tester.pump(const Duration(milliseconds: 20));
|
||||
await gesture.moveBy(const Offset(100.0, 0.0));
|
||||
expect(find.text('PUSH'), findsNothing);
|
||||
expect(find.text('HELLO'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 20));
|
||||
expect(find.text('PUSH'), findsOneWidget);
|
||||
expect(find.text('HELLO'), findsOneWidget);
|
||||
final Offset helloPosition2 = tester.getCenter(find.text('HELLO'));
|
||||
expect(helloPosition1.dx, lessThan(helloPosition2.dx));
|
||||
expect(helloPosition1.dy, helloPosition2.dy);
|
||||
expect(Theme.of(tester.element(find.text('HELLO'))).platform, TargetPlatform.android);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
platform: TargetPlatform.iOS,
|
||||
pageTransitionsTheme: pageTransitionsTheme,
|
||||
),
|
||||
routes: routes,
|
||||
),
|
||||
);
|
||||
// Now, let the theme animation run through.
|
||||
// This takes three frames (including the first one above):
|
||||
// 1. Start the Theme animation. It's at t=0 so everything else is identical.
|
||||
// 2. Start any animations that are informed by the Theme, for example, the
|
||||
// DefaultTextStyle, on the first frame that the theme is not at t=0. In
|
||||
// this case, it's at t=1.0 of the theme animation, so this is also the
|
||||
// frame in which the theme animation ends.
|
||||
// 3. End all the other animations.
|
||||
expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 2);
|
||||
expect(Theme.of(tester.element(find.text('HELLO'))).platform, TargetPlatform.iOS);
|
||||
final Offset helloPosition3 = tester.getCenter(find.text('HELLO'));
|
||||
expect(helloPosition3, helloPosition2);
|
||||
expect(find.text('PUSH'), findsOneWidget);
|
||||
expect(find.text('HELLO'), findsOneWidget);
|
||||
await gesture.moveBy(const Offset(100.0, 0.0));
|
||||
await tester.pump(const Duration(milliseconds: 20));
|
||||
expect(find.text('PUSH'), findsOneWidget);
|
||||
expect(find.text('HELLO'), findsOneWidget);
|
||||
final Offset helloPosition4 = tester.getCenter(find.text('HELLO'));
|
||||
expect(helloPosition3.dx, lessThan(helloPosition4.dx));
|
||||
expect(helloPosition3.dy, helloPosition4.dy);
|
||||
await gesture.moveBy(const Offset(500.0, 0.0));
|
||||
await gesture.up();
|
||||
expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 3);
|
||||
expect(find.text('PUSH'), findsOneWidget);
|
||||
expect(find.text('HELLO'), findsNothing);
|
||||
|
||||
await tester.tap(find.text('PUSH'));
|
||||
expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 2);
|
||||
expect(find.text('PUSH'), findsNothing);
|
||||
expect(find.text('HELLO'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user