Skip to content

Commit 7cbe45f

Browse files
authored
feat: Page.pop_views_until() to pop multiple views and return a result to the destination view (#6347)
* feat: Add pop_until_with_result to Page. Add the Page.pop_until_with_result() method and ViewPopResultEvent to support popping multiple views and returning a result to the destination view — the Flet equivalent of Flutter's Navigator.popUntilWithResult. Fixes #6326 * docs: add pop_until_with_result demo GIF Add animated GIF demonstrating the pop_until_with_result feature for the navigation-routing documentation assets. * refactor: rename to pop_views_until, based on feedback from @ndonkoHenri Rename pop_until_with_result() to pop_views_until(), on_view_pop_result to on_views_pop_until, and ViewPopResultEvent to ViewsPopUntilEvent. Requested by @FeodorFitsner in the PR review. * test: replace unit tests with integration test for pop_views_until Remove standalone unit test file and add integration test to test_routing_navigation.py following the existing test pattern, as suggested by @ndonkoHenri in PR review. * fix: use distinct text in SnackBar to avoid duplicate match in integration test The SnackBar and result_text both contained "Result: Flow completed!", causing find_by_text to match 2 elements instead of 1 in the CI integration test. * fix: align example and test with v0.85.0 directory structure Convert pop_views_until example from single file to subdirectory with main.py + pyproject.toml, matching the new example structure introduced in v0.84.0. Fix flet_app_main references to use module.main format consistent with upstream. * docs: add changelog entry for pop_views_until Add new feature record to CHANGELOG.md for v0.85.0 as required by the release branch CI check. * docs: update docstrings to reST cross-refs and fix changelog format Use :class:, :meth:, :attr: reST roles in docstrings instead of mkdocs-style cross-references, per docs-conventions guidelines. Remove Flutter reference from pop_views_until docstring. Fix CHANGELOG author format to plain. Requested by @ndonkoHenri in PR review. * docs: add pop_views_until section to navigation and routing page Add example and description for pop_views_until to the cookbook navigation-and-routing docs page. Requested by @ndonkoHenri in PR review.
1 parent 141a876 commit 7cbe45f

File tree

9 files changed

+315
-10
lines changed

9 files changed

+315
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* Add `ft.use_dialog()` hook for declarative dialog management from within `@ft.component` functions, with frozen-diff reactive updates and automatic open/close lifecycle ([#6335](https://github.com/flet-dev/flet/pull/6335)) by @FeodorFitsner.
66
* Add `scrollable`, `pin_leading_to_top`, and `pin_trailing_to_bottom` properties to `NavigationRail` for scrollable content with optional pinned leading/trailing controls ([#1923](https://github.com/flet-dev/flet/issues/1923), [#6356](https://github.com/flet-dev/flet/pull/6356)) by @ndonkoHenri.
7+
* Add `Page.pop_views_until()` to pop multiple views and return a result to the destination view ([#6326](https://github.com/flet-dev/flet/issues/6326), [#6347](https://github.com/flet-dev/flet/pull/6347)) by @brunobrown.
78

89
### Improvements
910

client/pubspec.lock

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,10 @@ packages:
157157
dependency: transitive
158158
description:
159159
name: characters
160-
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
160+
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
161161
url: "https://pub.dev"
162162
source: hosted
163-
version: "1.4.1"
163+
version: "1.4.0"
164164
charcode:
165165
dependency: transitive
166166
description:
@@ -359,7 +359,7 @@ packages:
359359
path: "../packages/flet"
360360
relative: true
361361
source: path
362-
version: "0.85.0"
362+
version: "0.82.2"
363363
flet_ads:
364364
dependency: "direct main"
365365
description:
@@ -911,18 +911,18 @@ packages:
911911
dependency: transitive
912912
description:
913913
name: matcher
914-
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
914+
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
915915
url: "https://pub.dev"
916916
source: hosted
917-
version: "0.12.19"
917+
version: "0.12.17"
918918
material_color_utilities:
919919
dependency: transitive
920920
description:
921921
name: material_color_utilities
922-
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
922+
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
923923
url: "https://pub.dev"
924924
source: hosted
925-
version: "0.13.0"
925+
version: "0.11.1"
926926
media_kit:
927927
dependency: transitive
928928
description:
@@ -1628,10 +1628,10 @@ packages:
16281628
dependency: transitive
16291629
description:
16301630
name: test_api
1631-
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
1631+
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
16321632
url: "https://pub.dev"
16331633
source: hosted
1634-
version: "0.7.10"
1634+
version: "0.7.7"
16351635
torch_light:
16361636
dependency: transitive
16371637
description:
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import asyncio
2+
3+
import flet as ft
4+
5+
6+
def main(page: ft.Page):
7+
page.title = "Routes Example"
8+
9+
result_text = ft.Text("No result yet", size=18)
10+
11+
def route_change():
12+
page.views.clear()
13+
14+
# Home View (/)
15+
page.views.append(
16+
ft.View(
17+
route="/",
18+
controls=[
19+
ft.AppBar(title=ft.Text("Home"), bgcolor=ft.Colors.SURFACE_BRIGHT),
20+
result_text,
21+
ft.Button(
22+
"Start flow",
23+
on_click=lambda _: asyncio.create_task(
24+
page.push_route("/step1")
25+
),
26+
),
27+
],
28+
)
29+
)
30+
31+
if page.route == "/step1" or page.route == "/step2" or page.route == "/step3":
32+
page.views.append(
33+
ft.View(
34+
route="/step1",
35+
controls=[
36+
ft.AppBar(
37+
title=ft.Text("Step 1"),
38+
bgcolor=ft.Colors.SURFACE_BRIGHT,
39+
),
40+
ft.Text("Step 1 of the flow"),
41+
ft.Button(
42+
"Go to Step 2",
43+
on_click=lambda _: asyncio.create_task(
44+
page.push_route("/step2")
45+
),
46+
),
47+
],
48+
)
49+
)
50+
51+
if page.route == "/step2" or page.route == "/step3":
52+
page.views.append(
53+
ft.View(
54+
route="/step2",
55+
controls=[
56+
ft.AppBar(
57+
title=ft.Text("Step 2"),
58+
bgcolor=ft.Colors.SURFACE_BRIGHT,
59+
),
60+
ft.Text("Step 2 of the flow"),
61+
ft.Button(
62+
"Go to Step 3",
63+
on_click=lambda _: asyncio.create_task(
64+
page.push_route("/step3")
65+
),
66+
),
67+
],
68+
)
69+
)
70+
71+
if page.route == "/step3":
72+
page.views.append(
73+
ft.View(
74+
route="/step3",
75+
controls=[
76+
ft.AppBar(
77+
title=ft.Text("Step 3 (Final)"),
78+
bgcolor=ft.Colors.SURFACE_BRIGHT,
79+
),
80+
ft.Text("Flow complete!"),
81+
ft.Button(
82+
"Finish and go Home",
83+
on_click=lambda _: asyncio.create_task(
84+
page.pop_views_until("/", result="Flow completed!")
85+
),
86+
),
87+
],
88+
)
89+
)
90+
91+
page.update()
92+
93+
def on_pop_result(e: ft.ViewsPopUntilEvent):
94+
result_text.value = f"Result: {e.result}"
95+
page.show_dialog(ft.SnackBar(ft.Text(f"Got result: {e.result}")))
96+
page.update()
97+
98+
async def view_pop(e: ft.ViewPopEvent):
99+
if e.view is not None:
100+
page.views.remove(e.view)
101+
top_view = page.views[-1]
102+
await page.push_route(top_view.route)
103+
104+
page.on_route_change = route_change
105+
page.on_view_pop = view_pop
106+
page.on_views_pop_until = on_pop_result
107+
108+
route_change()
109+
110+
111+
if __name__ == "__main__":
112+
ft.run(main)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[project]
2+
name = "apps-routing-navigation-pop-views-until"
3+
version = "1.0.0"
4+
description = "Pops multiple views from the navigation stack and returns a result to the destination view."
5+
requires-python = ">=3.10"
6+
keywords = ["apps", "routing", "navigation", "pop", "views", "result", "async"]
7+
authors = [{ name = "Flet team", email = "hello@flet.dev" }]
8+
dependencies = ["flet"]
9+
10+
[dependency-groups]
11+
dev = ["flet-cli", "flet-desktop", "flet-web"]
12+
13+
[tool.flet.gallery]
14+
categories = ["Apps/Navigation"]
15+
16+
[tool.flet.metadata]
17+
title = "Pop views until"
18+
controls = ["View", "AppBar", "Button", "Text", "SnackBar"]
19+
layout_pattern = "multi-step-flow"
20+
complexity = "intermediate"
21+
features = ["routing", "view stack", "pop views until", "result passing", "async"]
22+
23+
[tool.flet]
24+
org = "dev.flet"
25+
company = "Flet"
26+
copyright = "Copyright (C) 2023-2026 by Flet"

sdk/python/packages/flet/integration_tests/examples/apps/test_routing_navigation.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from examples.apps.routing_navigation.home_store import main as home_store
77
from examples.apps.routing_navigation.initial_route import main as initial_route
88
from examples.apps.routing_navigation.pop_view_confirm import main as pop_view_confirm
9+
from examples.apps.routing_navigation.pop_views_until import main as pop_views_until
910
from examples.apps.routing_navigation.route_change_event import (
1011
main as route_change_event,
1112
)
@@ -185,3 +186,59 @@ async def test_drawer_navigation(flet_app_function: ftt.FletTestApp):
185186
# Verify home view
186187
home_text = await flet_app_function.tester.find_by_text("Welcome to Home Page")
187188
assert home_text.count == 1
189+
190+
191+
@pytest.mark.parametrize(
192+
"flet_app_function",
193+
[{"flet_app_main": pop_views_until.main}],
194+
indirect=True,
195+
)
196+
@pytest.mark.asyncio(loop_scope="function")
197+
async def test_pop_views_until(flet_app_function: ftt.FletTestApp):
198+
# Verify initial view
199+
button = await flet_app_function.tester.find_by_text_containing("Start flow")
200+
assert button.count == 1
201+
result_text = await flet_app_function.tester.find_by_text("No result yet")
202+
assert result_text.count == 1
203+
204+
# Navigate to Step 1
205+
await flet_app_function.tester.tap(button)
206+
await flet_app_function.tester.pump_and_settle()
207+
step1_text = await flet_app_function.tester.find_by_text("Step 1 of the flow")
208+
assert step1_text.count == 1
209+
210+
# Navigate to Step 2
211+
step2_button = await flet_app_function.tester.find_by_text_containing(
212+
"Go to Step 2"
213+
)
214+
assert step2_button.count == 1
215+
await flet_app_function.tester.tap(step2_button)
216+
await flet_app_function.tester.pump_and_settle()
217+
step2_text = await flet_app_function.tester.find_by_text("Step 2 of the flow")
218+
assert step2_text.count == 1
219+
220+
# Navigate to Step 3
221+
step3_button = await flet_app_function.tester.find_by_text_containing(
222+
"Go to Step 3"
223+
)
224+
assert step3_button.count == 1
225+
await flet_app_function.tester.tap(step3_button)
226+
await flet_app_function.tester.pump_and_settle()
227+
final_text = await flet_app_function.tester.find_by_text("Flow complete!")
228+
assert final_text.count == 1
229+
230+
# Click "Finish and go Home" — triggers pop_views_until
231+
finish_button = await flet_app_function.tester.find_by_text_containing(
232+
"Finish and go Home"
233+
)
234+
assert finish_button.count == 1
235+
await flet_app_function.tester.tap(finish_button)
236+
await flet_app_function.tester.pump_and_settle()
237+
238+
# Verify back at Home with result
239+
result_text = await flet_app_function.tester.find_by_text("Result: Flow completed!")
240+
assert result_text.count == 1
241+
242+
# Verify we can start the flow again
243+
button = await flet_app_function.tester.find_by_text_containing("Start flow")
244+
assert button.count == 1

sdk/python/packages/flet/src/flet/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@
410410
PlatformBrightnessChangeEvent,
411411
RouteChangeEvent,
412412
ViewPopEvent,
413+
ViewsPopUntilEvent,
413414
)
414415
from flet.controls.painting import (
415416
Paint,
@@ -1073,6 +1074,7 @@
10731074
"VerticalDivider",
10741075
"View",
10751076
"ViewPopEvent",
1077+
"ViewsPopUntilEvent",
10761078
"VisualDensity",
10771079
"Wakelock",
10781080
"WebBrowserName",

0 commit comments

Comments
 (0)