Skip to content

Commit 178f16a

Browse files
fix: resolve component-wrapped views in Page root/top view accessors (#6414)
* fix: resolve component-wrapped views in Page root/top view accessors (#6413) Under `page.render_views()`, `self.views` is a `Component` (or a single `View` once unwrapped), not a `list[View]`. `__root_view()` / `__top_view()` called `len(self.views)` directly, breaking `page.show_drawer()`, `close_drawer()`, and every page-level accessor backed by the root view (appbar, drawer, navigation_bar, controls, ...). Add `__resolved_views()` that unwraps component wrappers and normalizes a single-View return into a one-element list, and route both view accessors through it. Also adds a declarative `NavigationDrawer` example under `examples/apps/declarative/navigation_drawer` covering the repro from the issue. * docs: changelog entry for #6414
1 parent fb3708d commit 178f16a

4 files changed

Lines changed: 91 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* Fix `CodeEditor` background not filling the entire area when `expand=True` ([#6407](https://github.com/flet-dev/flet/pull/6407)) by @FeodorFitsner.
1818
* Handle unbounded width in `ResponsiveRow` with an explicit error, treat child controls with `col=0` as hidden, and clarify `Container` expansion behavior when `alignment` is set ([#1951](https://github.com/flet-dev/flet/issues/1951), [#3805](https://github.com/flet-dev/flet/issues/3805), [#5209](https://github.com/flet-dev/flet/issues/5209), [#6354](https://github.com/flet-dev/flet/pull/6354)) by @ndonkoHenri.
1919
* Fix `find_platform_image` selecting incompatible icon formats (e.g. `.icns` on Windows) by ranking glob results per target platform ([#6381](https://github.com/flet-dev/flet/pull/6381)) by @HG-ha.
20+
* Fix `Page.show_drawer()`, `close_drawer()`, and root/top view accessors (`appbar`, `drawer`, `navigation_bar`, `controls`, ...) failing with `TypeError` under `Page.render_views()` by unwrapping component-wrapped views and normalizing single-view returns ([#6413](https://github.com/flet-dev/flet/issues/6413), [#6414](https://github.com/flet-dev/flet/pull/6414)) by @FeodorFitsner.
2021
* Fix `auto_scroll` on scrollable controls silently doing nothing unless `scroll` was also explicitly set ([#6397](https://github.com/flet-dev/flet/issues/6397), [#6404](https://github.com/flet-dev/flet/pull/6404)) by @ndonkoHenri.
2122

2223
### Other changes
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import flet as ft
2+
3+
4+
@ft.component
5+
def App():
6+
selected, set_selected = ft.use_state(0)
7+
8+
pages = [
9+
("Home", ft.Icons.HOME_OUTLINED, ft.Icons.HOME),
10+
("Store", ft.Icons.STORE_OUTLINED, ft.Icons.STORE),
11+
("About", ft.Icons.INFO_OUTLINED, ft.Icons.INFO),
12+
]
13+
14+
async def show_drawer():
15+
await ft.context.page.show_drawer()
16+
17+
async def handle_change(e: ft.Event[ft.NavigationDrawer]):
18+
set_selected(e.control.selected_index)
19+
await ft.context.page.close_drawer()
20+
21+
return ft.View(
22+
appbar=ft.AppBar(
23+
title=ft.Text(pages[selected][0]),
24+
leading=ft.IconButton(ft.Icons.MENU, on_click=show_drawer),
25+
bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST,
26+
),
27+
drawer=ft.NavigationDrawer(
28+
selected_index=selected,
29+
on_change=handle_change,
30+
controls=[
31+
ft.Container(height=12),
32+
*[
33+
ft.NavigationDrawerDestination(
34+
label=label, icon=icon, selected_icon=sel
35+
)
36+
for label, icon, sel in pages
37+
],
38+
],
39+
),
40+
controls=[
41+
ft.SafeArea(content=ft.Text(f"Welcome to {pages[selected][0]}", size=24))
42+
],
43+
)
44+
45+
46+
def main(page: ft.Page):
47+
page.render_views(App)
48+
49+
50+
if __name__ == "__main__":
51+
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-declarative-navigation-drawer"
3+
version = "1.0.0"
4+
description = "Declarative app with a NavigationDrawer whose selected destination drives the page content."
5+
requires-python = ">=3.10"
6+
keywords = ["apps", "declarative", "navigation drawer", "state"]
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/Declarative"]
15+
16+
[tool.flet.metadata]
17+
title = "Declarative navigation drawer"
18+
controls = ["View", "AppBar", "NavigationDrawer", "NavigationDrawerDestination", "SafeArea", "Text", "IconButton"]
19+
layout_pattern = "center-stage"
20+
complexity = "basic"
21+
features = ["drawer navigation", "state"]
22+
23+
[tool.flet]
24+
org = "dev.flet"
25+
company = "Flet"
26+
copyright = "Copyright (C) 2023-2026 by Flet"

sdk/python/packages/flet/src/flet/controls/base_page.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Union,
88
)
99

10+
from flet.components.public_utils import unwrap_component
1011
from flet.controls.adaptive_control import AdaptiveControl
1112
from flet.controls.animation import AnimationCurve
1213
from flet.controls.base_control import BaseControl, control
@@ -263,6 +264,12 @@ def handle_page_size(e):
263264
_overlay: "Overlay" = field(default_factory=lambda: Overlay())
264265
_dialogs: "Dialogs" = field(default_factory=lambda: Dialogs())
265266

267+
def __resolved_views(self) -> list[View]:
268+
views = unwrap_component(self.views)
269+
if isinstance(views, View):
270+
return [views]
271+
return [unwrap_component(v) for v in views]
272+
266273
def __root_view(self) -> View:
267274
"""
268275
Return the root view of this page container.
@@ -274,9 +281,10 @@ def __root_view(self) -> View:
274281
RuntimeError: If no views are available.
275282
"""
276283

277-
if len(self.views) == 0:
284+
views = self.__resolved_views()
285+
if len(views) == 0:
278286
raise RuntimeError("views list is empty.")
279-
return self.views[0]
287+
return views[0]
280288

281289
def __top_view(self) -> View:
282290
"""
@@ -289,9 +297,10 @@ def __top_view(self) -> View:
289297
RuntimeError: If no views are available.
290298
"""
291299

292-
if len(self.views) == 0:
300+
views = self.__resolved_views()
301+
if len(views) == 0:
293302
raise RuntimeError("views list is empty.")
294-
return self.views[-1]
303+
return views[-1]
295304

296305
def update(self, *controls: Control) -> None:
297306
"""

0 commit comments

Comments
 (0)