Skip to content

Commit 803b526

Browse files
justinmcsfshaza2parlough
authored
Predictive back transition breaking change announcement (#12268)
This PR spawned out of flutter/flutter#165832 (comment). That PR will change the default page transition and turn on predictive back by default. While there is no API breakage, users may encounter test breakages in tests that indirectly depend on the default page transition and/or its duration. Several of these kinds of breakages were encountered in flutter/flutter#165832 in the framework tests, customer tests, and Google tests. This PR should explain: * What predictive back is. * How to make sure an app supports predictive back. * What the new Fade Fowards page transition looks like. * How to manually change the page transition back to the old one. * How to write tests that don't depend on the page transition and its duration. _Issues fixed by this PR (if any):_ n/a _PRs or commits this PR depends on (if any):_ flutter/flutter#165832 ## Presubmit checklist - [x] If you are unwilling, or unable, to sign the CLA, even for a _tiny_, one-word PR, please file an issue instead of a PR. - [x] If this PR is not meant to land until a future stable release, mark it as draft with an explanation. - [x] This PR follows the [Google Developer Documentation Style Guidelines](https://developers.google.com/style)—for example, it doesn't use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first-person pronouns). - [x] This PR uses [semantic line breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks) of 80 characters or fewer. --------- Co-authored-by: Shams Zakhour <[email protected]> Co-authored-by: Parker Lougheed <[email protected]>
1 parent dd25fc6 commit 803b526

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
---
2+
title: The default page transition on Android is now PredictiveBackPageTransitionsBuilder
3+
description: >-
4+
Android's default page transition has been updated to match the
5+
platform and to support predictive back.
6+
---
7+
8+
## Summary
9+
10+
The default page transition on Android has been updated from
11+
[`ZoomPageTransitionsBuilder`][] to [`PredictiveBackPageTransitionsBuilder`][].
12+
When not using predictive back, this falls back to
13+
[`FadeForwardsPageTransitionsBuilder`][].
14+
15+
## Context
16+
17+
Android has been rolling out a feature called predictive back, where performing
18+
a back gesture allows the user to peek at the previous route or app and possibly
19+
cancel the navigation. Flutter added support for this with the [`PopScope`][]
20+
widget followed by [`PredictiveBackPageTransitionsBuilder`][].
21+
22+
In the meantime, Android also updated its default page transition. Flutter added
23+
support for this with [`FadeForwardsPageTransitionsBuilder`][].
24+
25+
## Description of change
26+
27+
With this change, [`PredictiveBackPageTransitionsBuilder`][] has replaced
28+
[`ZoomPageTransitionsBuilder`][] as the default page transition on Android.
29+
During a normal page transition without a predictive back gesture, users
30+
see the new [`FadeForwardsPageTransitionsBuilder`][] as the default page
31+
transition. When using a predictive back gesture, the page animates along
32+
with the gesture and allows canceling or committing to the back navigation.
33+
34+
In this example, no page transition is explicitly given, so the
35+
[default]({{site.github}}/flutter/flutter/blob/e983e4bd81f29b17215057fa5c9f46f96cbaf183/packages/flutter/lib/src/material/page_transitions_theme.dart#L806-L813)
36+
is set to [`PredictiveBackPageTransitionsBuilder`][] in the theme on Android.
37+
38+
```dart
39+
MaterialApp(
40+
theme: ThemeData(
41+
brightness: Brightness.light,
42+
),
43+
home: const PageOne(),
44+
);
45+
```
46+
47+
## Migration guide
48+
49+
If you want to keep your app's page transition on the old
50+
[`ZoomPageTransitionsBuilder`][], you can simply set your page transition
51+
explicitly in your app's theme. Keep in mind that you will not be able to
52+
support predictive back route transitions.
53+
54+
Code before migration:
55+
56+
```dart
57+
return MaterialApp(
58+
theme: ThemeData(
59+
brightness: Brightness.light,
60+
// pageTransitionsTheme is the default.
61+
),
62+
home: const MyFirstScreen(),
63+
);
64+
```
65+
66+
Code after migration:
67+
68+
```dart
69+
MaterialApp(
70+
theme: ThemeData(
71+
// pageTransitionsTheme is explicitly set to the old transition on Android.
72+
pageTransitionsTheme: const PageTransitionsTheme(
73+
builders: {
74+
TargetPlatform.android: ZoomPageTransitionsBuilder(),
75+
},
76+
),
77+
),
78+
home: const MyFirstScreen(),
79+
);
80+
```
81+
82+
One side effect of changing the default transition is that the duration that it
83+
takes to transition between pages has increased from 300ms to 450ms. This might
84+
cause breakages in tests that depend on the previous transition duration.
85+
Fortunately, it's possible to use [`TransitionDurationObserver`][] to keep tests
86+
independent of whatever page transition is used.
87+
88+
Code before migration:
89+
90+
```dart
91+
testWidgets('example', (WidgetTester tester) async {
92+
await tester.pumpWidget(
93+
MaterialApp(
94+
onGenerateRoute: (RouteSettings settings) { ... },
95+
),
96+
);
97+
98+
expect(find.text('Page 1'), findsOneWidget);
99+
expect(find.text('Page 2'), findsNothing);
100+
101+
// Pump through the whole transition, hardcoded to 300ms.
102+
await tester.tap(find.text('Next'));
103+
await tester.pump(const Duration(milliseconds: 300));
104+
105+
expect(find.text('Page 1'), findsNothing);
106+
expect(find.text('Page 2'), findsOneWidget);
107+
});
108+
```
109+
110+
Code after migration:
111+
112+
```dart
113+
testWidgets('example', (WidgetTester tester) async {
114+
final TransitionDurationObserver observer = TransitionDurationObserver();
115+
116+
await tester.pumpWidget(
117+
MaterialApp(
118+
navigatorObservers: <NavigatorObserver>[observer],
119+
onGenerateRoute: (RouteSettings settings) { ... },
120+
),
121+
);
122+
123+
expect(find.text('Page 1'), findsOneWidget);
124+
expect(find.text('Page 2'), findsNothing);
125+
126+
// Pump through the whole transition independent of the duration.
127+
await tester.tap(find.text('Next'));
128+
await observer.pumpPastTransition(tester);
129+
130+
expect(find.text('Page 1'), findsNothing);
131+
expect(find.text('Page 2'), findsOneWidget);
132+
});
133+
```
134+
135+
It's even possible to write tests that need to pump part of the way through a
136+
page transition without depending on the exact duration.
137+
138+
Code before migration:
139+
140+
```dart
141+
testWidgets('example', (WidgetTester tester) async {
142+
await tester.pumpWidget(
143+
MaterialApp(
144+
onGenerateRoute: (RouteSettings settings) { ... },
145+
),
146+
);
147+
148+
expect(find.text('Page 1'), findsOneWidget);
149+
expect(find.text('Page 2'), findsNothing);
150+
151+
// Pump through half of the transition with a hardcoded value.
152+
await tester.tap(find.text('Back'));
153+
await tester.pump(const Duration(milliseconds: 150));
154+
155+
expect(find.text('Page 1'), findsOneWidget);
156+
expect(find.text('Page 2'), findsOneWidget);
157+
});
158+
```
159+
160+
Code after migration:
161+
162+
```dart
163+
testWidgets('example', (WidgetTester tester) async {
164+
final TransitionDurationObserver observer = TransitionDurationObserver();
165+
166+
await tester.pumpWidget(
167+
MaterialApp(
168+
navigatorObservers: <NavigatorObserver>[observer],
169+
onGenerateRoute: (RouteSettings settings) { ... },
170+
),
171+
);
172+
173+
expect(find.text('Page 1'), findsOneWidget);
174+
expect(find.text('Page 2'), findsNothing);
175+
176+
// Pump through half of the transition independent of the duration.
177+
await tester.tap(find.text('Back'));
178+
await tester.pump(observer.transitionDuration ~/ 2);
179+
180+
expect(find.text('Page 1'), findsOneWidget);
181+
expect(find.text('Page 2'), findsOneWidget);
182+
});
183+
```
184+
185+
## Timeline
186+
187+
Landed in version: 3.37.0-0.0.pre<br>
188+
In stable release: 3.38.0
189+
190+
## References
191+
192+
API documentation:
193+
194+
* [`ZoomPageTransitionsBuilder`][]
195+
* [`PredictiveBackPageTransitionsBuilder`][]
196+
* [`FadeForwardsPageTransitionsBuilder`][]
197+
* [`PopScope`][]
198+
* [`TransitionDurationObserver`][]
199+
200+
Relevant issues:
201+
202+
* [Android predictive back route transitions][]
203+
204+
Relevant PRs:
205+
206+
* [Predictive back route transitions by default][]
207+
208+
[`FadeForwardsPageTransitionsBuilder`]: {{site.api}}/flutter/material/FadeForwardsPageTransitionsBuilder-class.html
209+
[`PopScope`]: {{site.api}}/flutter/widgets/PopScope-class.html
210+
[`PredictiveBackPageTransitionsBuilder`]: {{site.api}}/flutter/material/PredictiveBackPageTransitionsBuilder-class.html
211+
[`TransitionDurationObserver`]: {{site.api}}/flutter/flutter_test/TransitionDurationObserver-class.html
212+
[`ZoomPageTransitionsBuilder`]: {{site.api}}/flutter/material/ZoomPageTransitionsBuilder-class.html
213+
214+
[Android predictive back route transitions]: {{site.github.com}}/flutter/flutter/issues/131961
215+
[Predictive back route transitions by default]: {{site.github}}/flutter/flutter/pull/165832

src/content/release/breaking-changes/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,13 @@ They're sorted by release and listed in alphabetical order:
5555
* [Deprecate `OverlayPortal.targetsRootOverlay`][]
5656
* [Deprecate `SemanticsProperties.focusable` and `SemanticsConfiguration.isFocusable`][]
5757
* [SnackBar with action no longer auto-dismisses][]
58+
* [The default page transition on Android is now `PredictiveBackPageTransitionBuilder`][]
5859

5960
[`CupertinoDynamicColor` wide gamut support]: /release/breaking-changes/wide-gamut-cupertino-dynamic-color
6061
[Deprecate `OverlayPortal.targetsRootOverlay`]: /release/breaking-changes/deprecate-overlay-portal-targets-root
6162
[Deprecate `SemanticsProperties.focusable` and `SemanticsConfiguration.isFocusable`]: /release/breaking-changes/deprecate-focusable
6263
[SnackBar with action no longer auto-dismisses]: /release/breaking-changes/snackbar-with-action-behavior-update
64+
[The default page transition on Android is now `PredictiveBackPageTransitionBuilder`]: /release/breaking-changes/default-android-page-transition
6365

6466
<a id="released-in-flutter-335" aria-hidden="true"></a>
6567
### Released in Flutter 3.35

0 commit comments

Comments
 (0)