In a Flutter 3.47 app, what must be in place for Android predictive back to preview the previous route, and why can a custom transition lose that preview?
answer
- manifest opt-in on the application tag
- Android 14 and above
- PredictiveBackPageTransitionsBuilder, the 3.38 default
- observer claims the gesture at start
- no claimant means pop on commit
basics
~20 sAdd android:enableOnBackInvokedCallback="true" to the manifest, run on Android 14 or later, and keep routes on PredictiveBackPageTransitionsBuilder, the default for MaterialPageRoute since 3.38. Custom builders never claim the gesture, so the route just pops on release.
solid answer
~50 sPredictive back plays the back animation while the finger is still down, so the app must know ahead of time whether back is allowed. Flutter needs three things: the `android:enableOnBackInvokedCallback="true"` opt-in on the manifest's `<application>` tag, a device on Android 14 or later, and a route whose transition comes from `PredictiveBackPageTransitionsBuilder` — the Android default in `PageTransitionsTheme` since 3.38. When the gesture starts, the engine reports it to `WidgetsBinding`, which asks each observer's `handleStartBackGesture`; the builder's internal detector claims it if its route is current and `popGestureEnabled` is true, then feeds progress into the route animation and commits or cancels on release. A `PageRouteBuilder`, a custom theme builder or a `fullscreenDialog` route claims nothing, so the binding just pops the route on commit with its ordinary reverse animation. Outside a gesture, the builder plays `FadeForwardsPageTransitionsBuilder`.
code
xml · 9 lines<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="dating_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true">
<!-- activities -->
</application>
</manifest>go deeper
Recall that predictive back needs the manifest flag and that MaterialPageRoute supports it on Android by default.
Explain why the decision must be ahead of time, and how canPop and fullscreenDialog stop the preview.
Trace a missing preview to a custom builder or PageRouteBuilder, and choose between native motion and a hand-written gesture observer.
Set policy for which screens may trade platform back behaviour for brand motion, including testing on Android 14+ gesture navigation.
## What predictive back is **Predictive back** is Android's back gesture that shows where back will lead **before** the user commits: as the finger moves in from the edge, the current screen shrinks or slides and the previous screen (or the home screen) appears behind it. Releasing past the threshold commits the back; dragging back to the edge cancels it. Because the animation starts the moment the gesture starts, the app cannot veto back after the fact — it has to have declared in advance whether back is allowed. That is why Flutter 3.16 deprecated `WillPopScope` in favour of the ahead-of-time `PopScope` with a `canPop` flag. ## What Flutter needs 1. **The manifest opt-in.** Flutter's docs add `android:enableOnBackInvokedCallback="true"` to the `<application>` tag in `android/app/src/main/AndroidManifest.xml`. 2. **Android 14 or later.** `PredictiveBackPageTransitionsBuilder` documents that its gesture-driven transition only runs on Android U (14) and above; elsewhere it behaves like its fallback. 3. **A route that uses the builder.** Since **3.38** the default `PageTransitionsTheme` maps `TargetPlatform.android` to `PredictiveBackPageTransitionsBuilder`, so every `MaterialPageRoute`, `MaterialPage` and go_router default page qualifies without code. 4. **A route that allows a pop gesture.** `popGestureEnabled` must be true: not the first route, not a `fullscreenDialog`, no `PopScope` with `canPop: false`, and no transition still running. ## How the pieces talk - The Android embedding sends `startBackGesture`, progress updates, and `commitBackGesture` or `cancelBackGesture` to the framework. - `WidgetsBinding` asks every `WidgetsBindingObserver` for `handleStartBackGesture`. The predictive-back builder wraps each route in a private detector that is such an observer; it answers true only when **its route is current and `popGestureEnabled`**. - Observers that claimed the start then receive `handleUpdateBackGestureProgress` (a `PredictiveBackEvent` with `progress`, `swipeEdge` and `touchOffset`), and finally `handleCommitBackGesture` or `handleCancelBackGesture`. - **If nobody claimed the start**, a commit falls through to `handlePopRoute()`: the Navigator pops normally, with the route's usual reverse animation after the finger lifts. - Separately, `WidgetsApp` tells Android through `SystemNavigator.setFrameworkHandlesBack` whether Flutter can pop anything. At the root route it reports false, so the system can show its own back-to-home preview. ## Why a custom transition loses the preview | Route | Claims the gesture? | What the user sees on a back swipe | |---|---|---| | `MaterialPageRoute`, default theme | yes | page follows the finger; release commits or cancels | | `MaterialPageRoute`, Android mapped to a custom or `ZoomPageTransitionsBuilder` | no | nothing moves until release, then a normal pop | | `PageRouteBuilder` / `CustomTransitionPage` | no | same: a normal pop after release | | `fullscreenDialog: true` | no (`popGestureEnabled` is false) | a normal pop after release | In a dating app whose match profile slides up through a `PageRouteBuilder`, Android users swiping back get no preview: the profile simply drops away after they let go. Options: - Keep the slide-up for **forward** navigation only by pushing a `MaterialPageRoute` and accepting the platform's predictive transition — the most native result. - Use **`PredictiveBackFullscreenPageTransitionsBuilder`** where a full-screen style suits the screen better. - Write a builder that listens for back gestures itself. The detector inside Flutter's builder is private, so this means a `WidgetsBindingObserver` of your own that claims the start and drives the route's animation — real work to keep correct. ## Diagnosing a missing preview 1. Confirm the device runs Android 14 or later with gesture navigation turned on. 2. Confirm `android:enableOnBackInvokedCallback="true"` is on the `<application>` tag of the app's own manifest. 3. Check what the top route is: `PageRouteBuilder`, `CustomTransitionPage`, or a Material route whose theme maps Android to something other than a predictive-back builder. 4. Check `popGestureEnabled` for that route: first route, `fullscreenDialog`, a `PopScope` with `canPop: false`, or a push still animating. 5. Check that the route is current; a gesture that starts while another route is being pushed is not claimed. ## Testing and version notes - The default Android push takes **450 ms** since 3.38 (it was 300 ms), which broke tests that pumped a fixed 300 ms. - A back button press or `Navigator.pop` never shows the gesture preview; the builder plays `FadeForwardsPageTransitionsBuilder` for anything that is not a gesture. - Verify on a real Android 14+ device or emulator with gesture navigation enabled; three-button navigation has no swipe to preview.
- Why can a PopScope with canPop: false not show a predictive-back preview and then cancel it?The preview begins when the gesture starts, before any Dart callback could decide. So the decision is read up front: `canPop: false` makes the route's pop disposition `doNotPop`, `popGestureEnabled` becomes false, the builder does not claim the gesture, and the route stays put. `onPopInvokedWithResult` is then called with `didPop` false.
- What makes Android show its back-to-home animation instead of popping a Flutter route?When no route can pop, `WidgetsApp` calls `SystemNavigator.setFrameworkHandlesBack(false)`, telling Android that Flutter will not consume back. The system then handles the gesture itself and can preview returning to the home screen.
saying these in an interview costs you the question
- Any route animation follows the back gesture once the manifest flag is set.
- WillPopScope is still the way to confirm before back on Android.
- Predictive back needs a transition package from pub.dev.
- Navigator.pop and the back button also show the gesture preview.
- Android still defaults to the Zoom transition, so previews need opting in.