Skip to content

Commit d1e2019

Browse files
authored
Predictive back route transitions by default (flutter#165832)
This PR turns on predictive back route transitions by default on supported Android devices. With flutter#154718, the default (PredictiveBackPageTransitionsBuilder) is the [shared element transition](https://developer.android.com/design/ui/mobile/guides/patterns/predictive-back#shared-element-transition). The [full screen transition](https://developer.android.com/design/ui/mobile/guides/patterns/predictive-back#full-screen-surfaces) is also available by using PredictiveBackFullScreenPageTransitionsBuilder. Original PR: flutter#146788 Depends on: flutter#154718 When this lands in stable, the docs should be updated: https://docs.flutter.dev/platform-integration/android/predictive-back
1 parent 8914c91 commit d1e2019

23 files changed

+388
-48
lines changed

examples/api/test/material/divider/divider.0_test.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ void main() {
1313
expect(find.byType(Divider), findsOneWidget);
1414

1515
// Divider is positioned horizontally.
16-
final Offset container = tester.getBottomLeft(find.byType(ColoredBox).first);
16+
final Offset container = tester.getBottomLeft(
17+
find
18+
.descendant(of: find.byType(example.DividerExample), matching: find.byType(ColoredBox))
19+
.first,
20+
);
1721
expect(container.dy, tester.getTopLeft(find.byType(Divider)).dy);
1822

1923
final Offset subheader = tester.getTopLeft(find.text('Subheader'));

examples/api/test/widgets/basic/listener.0_test.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@ void main() {
1616

1717
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
1818
await gesture.addPointer();
19-
await gesture.down(tester.getCenter(find.byType(ColoredBox)));
19+
await gesture.down(
20+
tester.getCenter(
21+
find.descendant(
22+
of: find.byType(example.ListenerExample),
23+
matching: find.byType(ColoredBox),
24+
),
25+
),
26+
);
2027
await tester.pump();
2128

2229
expect(find.text('1 presses\n0 releases'), findsOneWidget);

examples/api/test/widgets/basic/mouse_region.0_test.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,28 @@ void main() {
1818

1919
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
2020
await gesture.addPointer();
21-
await gesture.moveTo(tester.getCenter(find.byType(ColoredBox)));
21+
await gesture.moveTo(
22+
tester.getCenter(
23+
find.descendant(
24+
of: find.byType(example.MouseRegionExample),
25+
matching: find.byType(ColoredBox),
26+
),
27+
),
28+
);
2229
await tester.pump();
2330

2431
expect(find.text('1 Entries\n0 Exits'), findsOneWidget);
2532
expect(find.text('The cursor is here: (400.00, 328.00)'), findsOneWidget);
2633

27-
await gesture.moveTo(tester.getCenter(find.byType(ColoredBox)) + const Offset(50.0, 30.0));
34+
await gesture.moveTo(
35+
tester.getCenter(
36+
find.descendant(
37+
of: find.byType(example.MouseRegionExample),
38+
matching: find.byType(ColoredBox),
39+
),
40+
) +
41+
const Offset(50.0, 30.0),
42+
);
2843
await tester.pump();
2944

3045
expect(find.text('The cursor is here: (450.00, 358.00)'), findsOneWidget);

examples/api/test/widgets/heroes/hero.0_test.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,19 @@ void main() {
2424
expect(heroSize.height.roundToDouble(), 60.0);
2525

2626
// Jump to 50% into the transition.
27-
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
27+
await tester.pump(quarterTransition);
2828
heroSize = tester.getSize(find.byType(Container));
2929
expect(heroSize.width.roundToDouble(), 189.0);
3030
expect(heroSize.height.roundToDouble(), 146.0);
3131

3232
// Jump to 75% into the transition.
33-
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
33+
await tester.pump(quarterTransition);
3434
heroSize = tester.getSize(find.byType(Container));
3535
expect(heroSize.width.roundToDouble(), 199.0);
3636
expect(heroSize.height.roundToDouble(), 190.0);
3737

3838
// Jump to 100% into the transition.
39-
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
39+
await tester.pump(quarterTransition);
4040
heroSize = tester.getSize(find.byType(Container));
4141
expect(heroSize, const Size(200.0, 200.0));
4242

@@ -45,25 +45,25 @@ void main() {
4545
await tester.pump();
4646

4747
// Jump 25% into the transition (total length = 300ms)
48-
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
48+
await tester.pump(quarterTransition);
4949
heroSize = tester.getSize(find.byType(Container));
5050
expect(heroSize.width.roundToDouble(), 199.0);
5151
expect(heroSize.height.roundToDouble(), 190.0);
5252

5353
// Jump to 50% into the transition.
54-
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
54+
await tester.pump(quarterTransition);
5555
heroSize = tester.getSize(find.byType(Container));
5656
expect(heroSize.width.roundToDouble(), 189.0);
5757
expect(heroSize.height.roundToDouble(), 146.0);
5858

5959
// Jump to 75% into the transition.
60-
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
60+
await tester.pump(quarterTransition);
6161
heroSize = tester.getSize(find.byType(Container));
6262
expect(heroSize.width.roundToDouble(), 103.0);
6363
expect(heroSize.height.roundToDouble(), 60.0);
6464

6565
// Jump to 100% into the transition.
66-
await tester.pump(const Duration(milliseconds: 75)); // 25% of 300ms
66+
await tester.pump(quarterTransition);
6767
heroSize = tester.getSize(find.byType(Container));
6868
expect(heroSize, const Size(50.0, 50.0));
6969
});

examples/api/test/widgets/transitions/align_transition.0_test.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ import 'package:flutter_test/flutter_test.dart';
99
void main() {
1010
testWidgets('Shows flutter logo in transition', (WidgetTester tester) async {
1111
await tester.pumpWidget(const example.AlignTransitionExampleApp());
12-
expect(find.byType(ColoredBox), findsOneWidget);
12+
expect(
13+
find.descendant(
14+
of: find.byType(example.AlignTransitionExample),
15+
matching: find.byType(ColoredBox),
16+
),
17+
findsOneWidget,
18+
);
1319
expect(
1420
find.byWidgetPredicate(
1521
(Widget padding) => padding is Padding && padding.padding == const EdgeInsets.all(8.0),

examples/api/test/widgets/transitions/fade_transition.0_test.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@ void main() {
1313
await tester.pumpWidget(const example.FadeTransitionExampleApp());
1414

1515
expect(
16-
find.ancestor(of: find.byType(FlutterLogo), matching: find.byType(FadeTransition)),
16+
find.descendant(
17+
of: find.byType(example.FadeTransitionExample),
18+
matching: find.byType(FadeTransition),
19+
),
1720
findsOneWidget,
1821
);
1922
});
2023

2124
testWidgets('FadeTransition animates', (WidgetTester tester) async {
2225
await tester.pumpWidget(const example.FadeTransitionExampleApp());
2326

24-
final Finder fadeTransitionFinder = find.ancestor(
25-
of: find.byType(FlutterLogo),
27+
final Finder fadeTransitionFinder = find.descendant(
28+
of: find.byType(example.FadeTransitionExample),
2629
matching: find.byType(FadeTransition),
2730
);
2831

examples/api/test/widgets/transitions/slide_transition.0_test.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ void main() {
1313
expect(find.byType(Center), findsOneWidget);
1414
expect(find.byType(FlutterLogo), findsOneWidget);
1515
expect(find.byType(Padding), findsAtLeast(1));
16-
expect(find.byType(SlideTransition), findsOneWidget);
16+
expect(
17+
find.descendant(
18+
of: find.byType(example.SlideTransitionExample),
19+
matching: find.byType(SlideTransition),
20+
),
21+
findsOneWidget,
22+
);
1723
});
1824

1925
testWidgets('Animates repeatedly every 2 seconds', (WidgetTester tester) async {

packages/flutter/lib/src/material/page_transitions_theme.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:flutter/services.dart';
1717

1818
import 'color_scheme.dart';
1919
import 'colors.dart';
20+
import 'predictive_back_page_transitions_builder.dart';
2021
import 'theme.dart';
2122

2223
// Slides the page upwards and fades it in, starting from 1/4 screen
@@ -759,7 +760,12 @@ class FadeForwardsPageTransitionsBuilder extends PageTransitionsBuilder {
759760
final Color? backgroundColor;
760761

761762
/// The value of [transitionDuration] in milliseconds.
762-
static const int kTransitionMilliseconds = 800;
763+
///
764+
/// Eyeballed on a physical Pixel 9 running Android 16. This does not match
765+
/// the actual value used by native Android, which is 800ms, because native
766+
/// Android is using Material 3 Expressive springs that are not currently
767+
/// supported by Flutter. So for now at least, this is an approximation.
768+
static const int kTransitionMilliseconds = 450;
763769

764770
@override
765771
Duration get transitionDuration => const Duration(milliseconds: kTransitionMilliseconds);
@@ -1097,7 +1103,7 @@ class PageTransitionsTheme with Diagnosticable {
10971103

10981104
static const Map<TargetPlatform, PageTransitionsBuilder> _defaultBuilders =
10991105
<TargetPlatform, PageTransitionsBuilder>{
1100-
TargetPlatform.android: ZoomPageTransitionsBuilder(),
1106+
TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
11011107
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
11021108
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
11031109
TargetPlatform.windows: ZoomPageTransitionsBuilder(),

packages/flutter/test/material/bottom_app_bar_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ void main() {
191191
final BottomAppBar bottomAppBar = tester.widget(find.byType(BottomAppBar));
192192
expect(bottomAppBar.padding, customPadding);
193193
final Rect babRect = tester.getRect(find.byType(BottomAppBar));
194-
final Rect childRect = tester.getRect(find.byType(ColoredBox));
194+
final Rect childRect = tester.getRect(
195+
find.descendant(of: find.byType(BottomAppBar), matching: find.byType(ColoredBox)),
196+
);
195197
expect(childRect, const Rect.fromLTRB(250, 530, 550, 590));
196198
expect(babRect, const Rect.fromLTRB(240, 520, 560, 600));
197199
});

packages/flutter/test/material/expansion_tile_test.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,7 +1690,9 @@ void main() {
16901690
);
16911691

16921692
final Size materialAppSize = tester.getSize(find.byType(MaterialApp));
1693-
final Size titleSize = tester.getSize(find.byType(ColoredBox));
1693+
final Size titleSize = tester.getSize(
1694+
find.descendant(of: find.byType(ExpansionTile), matching: find.byType(ColoredBox)),
1695+
);
16941696

16951697
expect(titleSize.width, materialAppSize.width);
16961698
},
@@ -1713,7 +1715,9 @@ void main() {
17131715
);
17141716

17151717
final Size materialAppSize = tester.getSize(find.byType(MaterialApp));
1716-
final Size titleSize = tester.getSize(find.byType(ColoredBox));
1718+
final Size titleSize = tester.getSize(
1719+
find.descendant(of: find.byType(ExpansionTile), matching: find.byType(ColoredBox)),
1720+
);
17171721

17181722
expect(titleSize.width, materialAppSize.width - 32.0);
17191723
},

0 commit comments

Comments
 (0)