bug: Android Back with a dialog open over a View also pops the View
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 1-2 天
- 新手友好度
- 42/100
调研方向
Start in _handleSystemPopRoute() in packages/flet/lib/src/controls/page.dart, which runs before popRoute() in packages/flet/lib/src/routing/router_delegate.dart. Check whether the Navigator's top route is a pageless dialog before the can_pop and on_confirm_pop checks. Done when a widget test with two Views and an AlertDialog over the second, given one handlePopRoute(), closes only the dialog and sends no view_pop, and a second call pops the View.
由索引模型根据 Issue 内容生成。
描述
Duplicate Check
- I have searched the opened issues and there are no duplicates (#3353 is about
on_view_popfiring on a desktop dialog close, a different path)
Describe the bug
On Android, with two or more Views and a dialog open over the top View (an AlertDialog, a DatePicker, a TimePicker, a BottomSheet or a PopupMenuButton's menu), one Back key press (or a committed Back gesture) closes the dialog and pops the View under it. Material and Flutter's own Navigator close only the topmost route: the dialog.
Cause
PageControl._handleSystemPopRoute() (packages/flet/lib/src/controls/page.dart, added in #5915) is the popRouteHandler of SimpleRouterDelegate (packages/flet/lib/src/routing/router_delegate.dart), so it runs on every system Back before PopNavigatorRouterDelegateMixin.popRoute()'s Navigator.maybePop(). With views.length > 1, when the top View has can_pop true and no on_confirm_pop handler, it calls _markViewAsPopped() (the View leaves the Navigator's pages at once and view_pop is sent) and returns true, so the Navigator is never asked. It does not check for pageless routes above that View's page: dialogs, pickers, sheets and menus are pushed on the same Navigator (useRootNavigator: false in alert_dialog.dart, date_picker.dart, time_picker.dart), and when the View's page is removed the routes attached to it go with it.
The Android Back gesture ends there too: with a dialog on top the page route is not current, so PredictiveBackPageTransitionsBuilder's detector declines the gesture (_isEnabled is route.isCurrent && popGestureEnabled) and the commit falls back to handlePopRoute() → popRoute().
A View that has can_pop=False or an on_confirm_pop handler is not affected: _handleSystemPopRoute() returns null and maybePop() pops the dialog.
Code sample
import flet as ft
def main(page: ft.Page):
def build_views():
page.views.clear()
page.views.append(
ft.View(
route="/",
appbar=ft.AppBar(title=ft.Text("Home")),
controls=[
ft.Button("Open details", on_click=lambda: page.navigate("/details"))
],
)
)
if page.route == "/details":
page.views.append(
ft.View(
route="/details",
appbar=ft.AppBar(title=ft.Text("Details")),
controls=[
ft.Button(
"Open dialog",
on_click=lambda: page.show_dialog(
ft.AlertDialog(
title=ft.Text("Back should close only this"),
on_dismiss=lambda e: print("dialog dismissed"),
)
),
),
ft.Button(
"Open date picker",
on_click=lambda: page.show_dialog(ft.DatePicker()),
),
],
)
)
page.update()
def on_view_pop(e: ft.ViewPopEvent):
print("view_pop", e.route)
page.views.pop()
page.navigate(page.views[-1].route)
page.on_route_change = lambda e: build_views()
page.on_view_pop = on_view_pop
build_views()
ft.run(main)
To reproduce
flet build apk(orflet debug android) and run it on an Android device or emulator.- Tap Open details, then Open dialog (or Open date picker).
- Press Back once (
adb shell input keyevent KEYCODE_BACK).
Expected behavior
The dialog closes and Details stays. A second Back pops Details.
Actual behavior
The dialog closes and Details is popped too: the app shows Home, and the log has view_pop /details with the dismiss.
Seen with the code above, built with flet build apk at 15854a0ac on an Android 14 emulator (API 34, gesture navigation): after one Back with the AlertDialog open, the screen showed Home and the log had view_pop /details then dialog dismissed; with the DatePicker open, Home and view_pop /details. The Back gesture (an edge swipe) did the same in the app where this was found.
Suggested fix
Let the Navigator close a pageless route first: in _handleSystemPopRoute(), return null when the Navigator's top route is not a page (so super.popRoute() → maybePop() pops it), before the can_pop / confirm_pop checks:
final navigator = _navigatorKey.currentState;
if (navigator != null && !_isTopRoutePageBased(navigator)) {
// A dialog, picker, sheet or menu is open over the top view:
// Navigator.maybePop() closes it, and the view stays.
return null;
}
bool _isTopRoutePageBased(NavigatorState navigator) {
Route<dynamic>? top;
navigator.popUntil((route) {
top = route;
return true; // stops at the top route: nothing is popped
});
return top?.settings is Page;
}
A test: two Views, an AlertDialog shown over the second, tester.binding.handlePopRoute() once: the dialog is gone, both Views remain and no view_pop was sent; a second handlePopRoute() sends view_pop for the second View.
Operating system
Android
Flet version
1.1 from the flet-1.1.0 branch at 15854a0ac; the same code is on main.
Regression
Likely since #5915 (December 2025), which added popRouteHandler: before it, popRoute() went straight to Navigator.maybePop().
Additional details
Found in an app that works around it by giving every pushed View an on_confirm_pop that calls confirm_pop(True): with a handler, _handleSystemPopRoute() returns null, maybePop() closes the dialog first, and the View's own pop still goes through onDidRemovePage → view_pop (the handler itself is never called while can_pop is true). With the fix above, that workaround is no longer needed.
Written by Claude Code (AI agent).
- 主要语言
- Python
- 星标
- 17.5k
- 派生
- 740
- 平均合并
- 1 天 8 小时
- 30 天内合并 PR
- 74
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
flet-dev/flet 的其他 Issue
-
feature request
难度 2/5 1-3 小时 新手友好度 66/100
flet-dev/flet#6985 · 3 条评论 · 1 个 reaction ·
维护者通常 1 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
Web: `TextField.focus()` after `on_submit` discards keys typed while the focus request is in flight未关闭
难度 4/5 3-5 天 新手友好度 45/100
维护者通常 1 天内回复
-
Web: TextField values set through the DOM (autofill-like) are lost; on_submit carries an empty value未关闭
难度 4/5 3-5 天 新手友好度 48/100
维护者通常 1 天内回复
-
feature request
难度 5/5 一周以上 新手友好度 25/100
维护者通常 1 天内回复
相似的 Issue
-
integration: logger
难度 2/5 1-3 小时 新手友好度 68/100
home-assistant/core#191147 · 1 条评论 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 76/100
维护者通常 1 天内回复
-
documentation p4
难度 1/5 1-3 小时 新手友好度 88/100
MillenniumDawn/focus-tree-creation-tool#417 ·
维护者通常 3 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 78/100
CharlesP44/Beem_Energy#96 ·
-
难度 2/5 1-3 小时 新手友好度 88/100
rheeloaded/paperpull#153 ·
维护者通常 1 天内回复