Skip to content

Fix embedded FletApp back gesture exiting host app#6715

Open
FeodorFitsner wants to merge 2 commits into
mainfrom
fix-embedded-back-navigation
Open

Fix embedded FletApp back gesture exiting host app#6715
FeodorFitsner wants to merge 2 commits into
mainfrom
fix-embedded-back-navigation

Conversation

@FeodorFitsner

@FeodorFitsner FeodorFitsner commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

A system/edge-swipe back gesture that lands on an embedded FletApp (an app rendered inside another Flet app via FletApp — e.g. a gallery host running example apps in-process) closed the whole host app instead of navigating back.

Root cause

This is Android's predictive-back path, not the legacy didPopRoute path.

Every WidgetsApp (MaterialApp/CupertinoApp) listens for NavigationNotification, and its default handler both calls SystemNavigator.setFrameworkHandlesBack(canHandlePop) and stops the notification. The embedded example's single-view Navigator reports canHandlePop: false, so the embedded WidgetsApp:

  1. told the OS the framework does not handle back, and
  2. swallowed the notification before it could reach the host Navigator — which would have re-reported canHandlePop: true, since it can pop the view embedding the app.

The OS then finished the activity → the app exits. didPopRoute never fired.

Fix

For an embedded page (backend.controlId != null), in packages/flet/lib/src/controls/page.dart:

  • onNavigationNotification: (_) => false — don't run WidgetsApp's default handler; let the notification bubble to the host, whose Navigator re-reports canHandlePop: true and whose WidgetsApp calls setFrameworkHandlesBack(true). This is what stops the OS from finishing the activity.
  • ChildBackButtonDispatcher chained to the host Router (instead of the default RootBackButtonDispatcher) — so once back actually reaches didPopRoute, an unhandled back propagates from the embedded Router to the host Router, which pops the view embedding the FletApp. This also preserves correct precedence for multi-view embedded apps: they pop their own inner view first, then fall through to the host.

Non-embedded (root) pages are unaffected: onNavigationNotification stays null (default handler) and no child dispatcher is created.

Testing

  • Verified on a real Android device (moto g15) and on an emulator: back-swipe on an embedded example now returns to the host view instead of exiting the app.
  • Verified against Flutter 3.44.7 source for both the NavigationNotification combining logic (Navigator) and the dispatcher fall-through (BackButtonDispatcher/ChildBackButtonDispatcher).
  • flutter analyze clean.

Notes

This makes an embedded app defer back-reporting to its host, which is correct for the only way FletApp embeds today (inside another Flet app with its own Navigator).

Summary by Sourcery

Ensure embedded Flet apps delegate system back handling to their host so edge-swipe back navigates within the host instead of exiting the entire app.

Bug Fixes:

  • Prevent Android system/edge-swipe back gestures on embedded FletApp pages from closing the host app by propagating back events to the host navigator.

Documentation:

  • Document the embedded FletApp back gesture fix in the main changelog and package changelog for version 0.86.3.

A system/edge-swipe back landing on an embedded FletApp (an app rendered
inside another Flet app via FletApp, e.g. a gallery host running example
apps in-process) closed the whole host app instead of navigating back.

Root cause is Android's predictive-back path, not the legacy didPopRoute
path: each WidgetsApp (MaterialApp/CupertinoApp) listens for
NavigationNotification and, by default, calls
SystemNavigator.setFrameworkHandlesBack(canHandlePop) and stops the
notification. The embedded app's single-view Navigator reports
canHandlePop=false, so the embedded WidgetsApp told the OS the framework
does not handle back AND swallowed the notification before it could reach
the host Navigator (which would have re-reported canHandlePop=true since
it can pop the embedding view). The OS then finished the activity.

Fix, for an embedded page (backend.controlId != null):
- Override onNavigationNotification to return false so the notification
  bubbles to the host, whose Navigator re-reports canHandlePop=true and
  whose WidgetsApp calls setFrameworkHandlesBack(true).
- Chain a ChildBackButtonDispatcher to the host Router (instead of the
  default RootBackButtonDispatcher) so that once the back actually reaches
  didPopRoute, an unhandled back propagates from the embedded Router to
  the host Router, which pops the view embedding the FletApp. This also
  preserves correct precedence for multi-view embedded apps (they pop
  their own inner view first).

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 24, 2026

Copy link
Copy Markdown

Deploying flet-website-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6338ec9
Status: ✅  Deploy successful!
Preview URL: https://be7247ef.flet-website-v2.pages.dev
Branch Preview URL: https://fix-embedded-back-navigation.flet-website-v2.pages.dev

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant