
Fixes: #115275 Fixes: #116127 Fixes: #126682 Continuing on: #139078 (Credits to @LowLevelSubmarine for his initial work!) When using `ZoomPageTransitionsBuilder`, which is the default for `ThemeData` with a `MaterialApp`, dark edges would show around the exiting page that was being zoomed out in the background. Other times, a scrim (what looked like a slightly transparent dark overlay over the page) would appear. After some experimenting it was concluded that, in the first case, this was because both pages don't fully fill the enclosing scaffold area during the transition and the color for filling the remaining space was set hard coded as `Colors.black`. The second case (scrim) happens when navigating from a page with an enclosing scaffold to a nested one, without a scaffold, unlike the first case that happens when both pages have a (different) enclosing scaffold, except this time it would be the hard coded color covering the page with a slight opacity reduction. ### Changes - Replaced the hard coded color for transition filling with the current `ThemeData.colorScheme.surface` - Added a RenderBox based test to verify the correct color is being used in the transition. ## Preview **Before, notice the dark outline flash when navigating to the first page and the scrim when navigating to the second:** https://github.com/user-attachments/assets/b4cc8658-1008-49f4-8553-abd5fcc72989 **After, using the theme relative color (in this case the default white) to replace the hard coded value:** https://github.com/user-attachments/assets/b70f42d2-6246-4964-99d1-34ff8051ab06 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation. - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
630 lines
22 KiB
Dart
630 lines
22 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
testWidgets('Default PageTransitionsTheme platform', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const MaterialApp(home: Text('home')));
|
|
final PageTransitionsTheme theme = Theme.of(tester.element(find.text('home'))).pageTransitionsTheme;
|
|
expect(theme.builders, isNotNull);
|
|
for (final TargetPlatform platform in TargetPlatform.values) {
|
|
switch (platform) {
|
|
case TargetPlatform.android:
|
|
case TargetPlatform.iOS:
|
|
case TargetPlatform.macOS:
|
|
expect(
|
|
theme.builders[platform],
|
|
isNotNull,
|
|
reason: 'theme builder for $platform is null',
|
|
);
|
|
case TargetPlatform.fuchsia:
|
|
case TargetPlatform.linux:
|
|
case TargetPlatform.windows:
|
|
expect(
|
|
theme.builders[platform],
|
|
isNull,
|
|
reason: 'theme builder for $platform is not null',
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
testWidgets('Default PageTransitionsTheme builds a CupertinoPageTransition', (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('page b'),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
expect(Theme.of(tester.element(find.text('push'))).platform, debugDefaultTargetPlatformOverride);
|
|
expect(find.byType(CupertinoPageTransition), findsOneWidget);
|
|
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('page b'), findsOneWidget);
|
|
expect(find.byType(CupertinoPageTransition), findsOneWidget);
|
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
|
|
|
testWidgets('Default PageTransitionsTheme builds a _ZoomPageTransition for android', (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('page b'),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
Finder findZoomPageTransition() {
|
|
return find.descendant(
|
|
of: find.byType(MaterialApp),
|
|
matching: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_ZoomPageTransition'),
|
|
);
|
|
}
|
|
|
|
expect(Theme.of(tester.element(find.text('push'))).platform, debugDefaultTargetPlatformOverride);
|
|
expect(findZoomPageTransition(), findsOneWidget);
|
|
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('page b'), findsOneWidget);
|
|
expect(findZoomPageTransition(), findsOneWidget);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
|
|
|
testWidgets('PageTransitionsTheme override builds a _OpenUpwardsPageTransition', (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('page b'),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
pageTransitionsTheme: const PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
TargetPlatform.android: OpenUpwardsPageTransitionsBuilder(), // creates a _OpenUpwardsPageTransition
|
|
},
|
|
),
|
|
),
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
Finder findOpenUpwardsPageTransition() {
|
|
return find.descendant(
|
|
of: find.byType(MaterialApp),
|
|
matching: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_OpenUpwardsPageTransition'),
|
|
);
|
|
}
|
|
|
|
expect(Theme.of(tester.element(find.text('push'))).platform, debugDefaultTargetPlatformOverride);
|
|
expect(findOpenUpwardsPageTransition(), findsOneWidget);
|
|
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('page b'), findsOneWidget);
|
|
expect(findOpenUpwardsPageTransition(), findsOneWidget);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
|
|
|
testWidgets('PageTransitionsTheme override builds a CupertinoPageTransition on android', (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('page b'),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
pageTransitionsTheme: const PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
|
},
|
|
),
|
|
),
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
expect(Theme.of(tester.element(find.text('push'))).platform, debugDefaultTargetPlatformOverride);
|
|
expect(find.byType(CupertinoPageTransition), findsOneWidget);
|
|
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('page b'), findsOneWidget);
|
|
expect(find.byType(CupertinoPageTransition), findsOneWidget);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
|
|
|
testWidgets('CupertinoPageTransition on android does not block gestures on backswipe', (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('page b'),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
pageTransitionsTheme: const PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
|
},
|
|
),
|
|
),
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
expect(Theme.of(tester.element(find.text('push'))).platform, debugDefaultTargetPlatformOverride);
|
|
expect(find.byType(CupertinoPageTransition), findsOneWidget);
|
|
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('page b'), findsOneWidget);
|
|
expect(find.byType(CupertinoPageTransition), findsOneWidget);
|
|
|
|
await tester.pumpAndSettle(const Duration(minutes: 1));
|
|
|
|
final TestGesture gesture = await tester.startGesture(const Offset(5.0, 100.0));
|
|
await gesture.moveBy(const Offset(400.0, 0.0));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
await tester.pumpAndSettle(const Duration(minutes: 1));
|
|
|
|
expect(find.text('push'), findsOneWidget);
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('page b'), findsOneWidget);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
|
|
|
testWidgets('PageTransitionsTheme override builds a _FadeUpwardsTransition', (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('page b'),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
pageTransitionsTheme: const PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), // creates a _FadeUpwardsTransition
|
|
},
|
|
),
|
|
),
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
Finder findFadeUpwardsPageTransition() {
|
|
return find.descendant(
|
|
of: find.byType(MaterialApp),
|
|
matching: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_FadeUpwardsPageTransition'),
|
|
);
|
|
}
|
|
|
|
expect(Theme.of(tester.element(find.text('push'))).platform, debugDefaultTargetPlatformOverride);
|
|
expect(findFadeUpwardsPageTransition(), findsOneWidget);
|
|
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('page b'), findsOneWidget);
|
|
expect(findFadeUpwardsPageTransition(), findsOneWidget);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
|
|
|
Widget boilerplate({
|
|
required bool themeAllowSnapshotting,
|
|
bool secondRouteAllowSnapshotting = true,
|
|
}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
useMaterial3: true,
|
|
pageTransitionsTheme: PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
TargetPlatform.android: ZoomPageTransitionsBuilder(
|
|
allowSnapshotting: themeAllowSnapshotting,
|
|
),
|
|
},
|
|
),
|
|
),
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
if (settings.name == '/') {
|
|
return MaterialPageRoute<Widget>(
|
|
builder: (_) => const Material(child: Text('Page 1')),
|
|
);
|
|
}
|
|
return MaterialPageRoute<Widget>(
|
|
builder: (_) => const Material(child: Text('Page 2')),
|
|
allowSnapshotting: secondRouteAllowSnapshotting,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
bool isTransitioningWithSnapshotting(WidgetTester tester, Finder of) {
|
|
final Iterable<Layer> layers = tester.layerListOf(
|
|
find.ancestor(of: of, matching: find.byType(SnapshotWidget)).first,
|
|
);
|
|
final bool hasOneOpacityLayer = layers.whereType<OpacityLayer>().length == 1;
|
|
final bool hasOneTransformLayer = layers.whereType<TransformLayer>().length == 1;
|
|
// When snapshotting is on, the OpacityLayer and TransformLayer will not be
|
|
// applied directly.
|
|
return !(hasOneOpacityLayer && hasOneTransformLayer);
|
|
}
|
|
|
|
testWidgets('ZoomPageTransitionsBuilder default route snapshotting behavior', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(themeAllowSnapshotting: true),
|
|
);
|
|
|
|
final Finder page1 = find.text('Page 1');
|
|
final Finder page2 = find.text('Page 2');
|
|
|
|
// Transitioning from page 1 to page 2.
|
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/2');
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
|
|
// Exiting route should be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page1), isTrue);
|
|
|
|
// Entering route should be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page2), isTrue);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Transitioning back from page 2 to page 1.
|
|
tester.state<NavigatorState>(find.byType(Navigator)).pop();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
|
|
// Exiting route should be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page2), isTrue);
|
|
|
|
// Entering route should be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page1), isTrue);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web.
|
|
|
|
testWidgets('ZoomPageTransitionsBuilder.allowSnapshotting can disable route snapshotting', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(themeAllowSnapshotting: false),
|
|
);
|
|
|
|
final Finder page1 = find.text('Page 1');
|
|
final Finder page2 = find.text('Page 2');
|
|
|
|
// Transitioning from page 1 to page 2.
|
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/2');
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
|
|
// Exiting route should not be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page1), isFalse);
|
|
|
|
// Entering route should not be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page2), isFalse);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Transitioning back from page 2 to page 1.
|
|
tester.state<NavigatorState>(find.byType(Navigator)).pop();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
|
|
// Exiting route should not be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page2), isFalse);
|
|
|
|
// Entering route should not be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page1), isFalse);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web.
|
|
|
|
testWidgets('Setting PageRoute.allowSnapshotting to false overrides ZoomPageTransitionsBuilder.allowSnapshotting = true', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
themeAllowSnapshotting: true,
|
|
secondRouteAllowSnapshotting: false,
|
|
),
|
|
);
|
|
|
|
final Finder page1 = find.text('Page 1');
|
|
final Finder page2 = find.text('Page 2');
|
|
|
|
// Transitioning from page 1 to page 2.
|
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/2');
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
|
|
// First route should be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page1), isTrue);
|
|
|
|
// Second route should not be snapshotted.
|
|
expect(isTransitioningWithSnapshotting(tester, page2), isFalse);
|
|
|
|
await tester.pumpAndSettle();
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web.
|
|
|
|
testWidgets('_ZoomPageTransition only causes child widget built once', (WidgetTester tester) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/58345
|
|
|
|
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++; // Increase [builtCount] each time the widget build
|
|
return TextButton(
|
|
child: const Text('pop'),
|
|
onPressed: () { Navigator.pop(context); },
|
|
);
|
|
},
|
|
),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
pageTransitionsTheme: const PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
TargetPlatform.android: ZoomPageTransitionsBuilder(), // creates a _ZoomPageTransition
|
|
},
|
|
),
|
|
),
|
|
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);
|
|
|
|
await tester.tap(find.text('pop'));
|
|
await tester.pumpAndSettle();
|
|
expect(builtCount, 1);
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
|
|
|
testWidgets('predictive back gestures pop the route on all platforms regardless of whether their transition handles predictive back', (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('page b'),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
expect(find.text('push'), findsOneWidget);
|
|
expect(find.text('page b'), findsNothing);
|
|
|
|
await tester.tap(find.text('push'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('push'), findsNothing);
|
|
expect(find.text('page b'), findsOneWidget);
|
|
|
|
// Start a system pop gesture.
|
|
final ByteData startMessage = const StandardMethodCodec().encodeMethodCall(
|
|
const MethodCall(
|
|
'startBackGesture',
|
|
<String, dynamic>{
|
|
'touchOffset': <double>[5.0, 300.0],
|
|
'progress': 0.0,
|
|
'swipeEdge': 0, // left
|
|
},
|
|
),
|
|
);
|
|
await binding.defaultBinaryMessenger.handlePlatformMessage(
|
|
'flutter/backgesture',
|
|
startMessage,
|
|
(ByteData? _) {},
|
|
);
|
|
await tester.pump();
|
|
|
|
expect(find.text('push'), findsNothing);
|
|
expect(find.text('page b'), findsOneWidget);
|
|
|
|
// Drag the system back gesture far enough to commit.
|
|
final ByteData updateMessage = const StandardMethodCodec().encodeMethodCall(
|
|
const MethodCall(
|
|
'updateBackGestureProgress',
|
|
<String, dynamic>{
|
|
'x': 100.0,
|
|
'y': 300.0,
|
|
'progress': 0.35,
|
|
'swipeEdge': 0, // left
|
|
},
|
|
),
|
|
);
|
|
await binding.defaultBinaryMessenger.handlePlatformMessage(
|
|
'flutter/backgesture',
|
|
updateMessage,
|
|
(ByteData? _) {},
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('push'), findsNothing);
|
|
expect(find.text('page b'), findsOneWidget);
|
|
|
|
// Commit the system back gesture.
|
|
final ByteData commitMessage = const StandardMethodCodec().encodeMethodCall(
|
|
const MethodCall(
|
|
'commitBackGesture',
|
|
),
|
|
);
|
|
await binding.defaultBinaryMessenger.handlePlatformMessage(
|
|
'flutter/backgesture',
|
|
commitMessage,
|
|
(ByteData? _) {},
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('push'), findsOneWidget);
|
|
expect(find.text('page b'), findsNothing);
|
|
}, variant: TargetPlatformVariant.all());
|
|
|
|
testWidgets('ZoomPageTransitionsBuilder uses theme color during transition effects', (WidgetTester tester) async {
|
|
// Color that is being tested for presence.
|
|
const Color themeTestSurfaceColor = Color.fromARGB(255, 195, 255, 0);
|
|
|
|
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
|
|
'/': (BuildContext context) => Material(
|
|
child: Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Home Page'),
|
|
),
|
|
body: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: <Widget>[
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pushNamed(context, '/scaffolded');
|
|
},
|
|
child: const Text('Route with scaffold!'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pushNamed(context, '/not-scaffolded');
|
|
},
|
|
child: const Text('Route with NO scaffold!'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
'/scaffolded': (BuildContext context) => Material(
|
|
child: Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Scaffolded Page'),
|
|
),
|
|
body: Center(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
},
|
|
child: const Text('Back to home route...'),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
'/not-scaffolded': (BuildContext context) => Material(
|
|
child: Center(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
},
|
|
child: const Text('Back to home route...'),
|
|
),
|
|
),
|
|
),
|
|
};
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(
|
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, surface: themeTestSurfaceColor),
|
|
pageTransitionsTheme: PageTransitionsTheme(
|
|
builders: <TargetPlatform, PageTransitionsBuilder>{
|
|
// Force all platforms to use ZoomPageTransitionsBuilder to test each one.
|
|
for (final TargetPlatform platform in TargetPlatform.values) platform: const ZoomPageTransitionsBuilder(),
|
|
},
|
|
),
|
|
),
|
|
routes: routes,
|
|
),
|
|
);
|
|
|
|
// Go to scaffolded page.
|
|
await tester.tap(find.text('Route with scaffold!'));
|
|
|
|
// Pump till animation is half-way through.
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds:75));
|
|
|
|
// Verify that the render box is painting the right color for scaffolded pages.
|
|
final RenderBox scaffoldedRenderBox = tester.firstRenderObject<RenderBox>(find.byType(MaterialApp));
|
|
// Expect the color to be at exactly 12.2% opacity at this time.
|
|
expect(scaffoldedRenderBox, paints..rect(color: themeTestSurfaceColor.withOpacity(0.122)));
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Go back home and then go to non-scaffolded page.
|
|
await tester.tap(find.text('Back to home route...'));
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.text('Route with NO scaffold!'));
|
|
|
|
// Pump till animation is half-way through.
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds:125));
|
|
|
|
// Verify that the render box is painting the right color for non-scaffolded pages.
|
|
final RenderBox nonScaffoldedRenderBox = tester.firstRenderObject<RenderBox>(find.byType(MaterialApp));
|
|
// Expect the color to be at exactly 59.6% opacity at this time.
|
|
expect(nonScaffoldedRenderBox, paints..rect(color: themeTestSurfaceColor.withOpacity(0.596)));
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Verify that the transition successfully completed.
|
|
expect(find.text('Back to home route...'), findsOneWidget);
|
|
}, variant: TargetPlatformVariant.all());
|
|
}
|