You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flet切换View时NavigationRail不显示问题求助

解决Flet中NavigationRail切换视图后消失的问题

我来帮你搞定这个问题!你遇到的核心问题是同一个Flet控件实例不能被同时添加到多个父容器里——你把同一个rail对象既加到了home_view的Row,又加到了test_view的Row,这就导致控件的归属混乱,切换视图时Rail就“失踪”了。

下面给你两种解决方案,按需选择:

方案1:持久化布局(推荐,更贴合NavigationRail的使用场景)

这种方式不需要用多View,而是把NavigationRail固定在页面左侧,右侧用一个容器来动态切换内容。Rail会一直显示,只更新右侧的内容区域,逻辑更简单。

修改后的代码:

import flet as ft

def main(page: ft.Page):
    page.title= "Utilidades Pintura"
    page.window_width = 600
    page.window_height = 500
    page.window_resizable = False

    # 创建一个容器,专门用来放当前页面的内容
    content_container = ft.Column(controls=[ft.Text("Home screen")])

    def on_rail_change(e):
        # 根据Rail选中的索引切换内容
        content_container.controls.clear()
        match e.control.selected_index:
            case 0:
                content_container.controls.append(ft.Text("Home screen"))
            case 1:
                content_container.controls.append(ft.Text("Test screen!"))
            case 2:
                content_container.controls.append(ft.Text("Settings screen!"))
        page.update()

    rail = ft.NavigationRail(
        selected_index=0,
        label_type=ft.NavigationRailLabelType.ALL,
        min_width=100,
        min_extended_width=400,
        group_alignment=-0.9,
        height=500,
        width=100,
        destinations=[
            ft.NavigationRailDestination(
                icon=ft.icons.FAVORITE_BORDER, selected_icon=ft.icons.FAVORITE, label="First"
            ),
            ft.NavigationRailDestination(
                icon_content=ft.Icon(ft.icons.BOOKMARK_BORDER),
                selected_icon_content=ft.Icon(ft.icons.BOOKMARK),
                label="Second",
            ),
            ft.NavigationRailDestination(
                icon=ft.icons.SETTINGS_OUTLINED,
                selected_icon_content=ft.Icon(ft.icons.SETTINGS),
                label_content=ft.Text("Settings"),
            ),
        ],
        on_change=on_rail_change,
    )

    # 页面主布局:左侧Rail + 右侧内容容器,expand=True让布局占满窗口
    page.add(ft.Row([rail, content_container], expand=True))

ft.app(target=main)

方案2:保留多View和路由机制

如果你想继续用路由和View的结构,那每个View都需要使用独立的NavigationRail实例,同时同步选中状态。我们可以封装一个创建Rail的函数,每次生成新的实例,避免重复使用同一个控件。

修改后的代码:

import flet as ft

# 封装创建NavigationRail的函数,每次调用都会生成新实例
def create_rail(selected_index, on_change):
    return ft.NavigationRail(
        selected_index=selected_index,
        label_type=ft.NavigationRailLabelType.ALL,
        min_width=100,
        min_extended_width=400,
        group_alignment=-0.9,
        height=500,
        width=100,
        destinations=[
            ft.NavigationRailDestination(
                icon=ft.icons.FAVORITE_BORDER, selected_icon=ft.icons.FAVORITE, label="First"
            ),
            ft.NavigationRailDestination(
                icon_content=ft.Icon(ft.icons.BOOKMARK_BORDER),
                selected_icon_content=ft.Icon(ft.icons.BOOKMARK),
                label="Second",
            ),
            ft.NavigationRailDestination(
                icon=ft.icons.SETTINGS_OUTLINED,
                selected_icon_content=ft.Icon(ft.icons.SETTINGS),
                label_content=ft.Text("Settings"),
            ),
        ],
        on_change=on_change,
    )

def main(page: ft.Page):
    page.title= "Utilidades Pintura"
    page.window_width = 600
    page.window_height = 500
    page.window_resizable = False

    def on_rail_change(e):
        # 根据选中的索引跳转到对应路由
        match e.control.selected_index:
            case 0:
                page.go("/")
            case 1:
                page.go("/test")
            case 2:
                page.go("/settings")
        page.update()

    def route_change(route):
        page.views.clear()
        # 根据当前路由确定Rail的选中索引和页面内容
        selected_idx = 0
        content = ft.Text("Home screen")
        if page.route == "/test":
            selected_idx = 1
            content = ft.Text("Test screen!")
        elif page.route == "/settings":
            selected_idx = 2
            content = ft.Text("Settings screen!")
        
        # 为当前View创建独立的Rail实例
        current_rail = create_rail(selected_idx, on_rail_change)
        page.views.append(
            ft.View(
                route=page.route,
                controls=[ft.Row([current_rail, content], expand=True)]
            )
        )
        page.update()

    def view_pop(view):
        page.views.pop()
        top_view = page.views[-1]
        page.go(top_view.route)

    page.on_route_change = route_change
    page.on_view_pop = view_pop
    page.go(page.route)

ft.app(target=main)

为什么你的原代码会出问题?

Flet的每个控件实例只能属于一个父容器,你把同一个rail对象同时添加到了home_view和test_view的Row里,当切换路由时,Flet会尝试把Rail从之前的父容器移动到新的父容器,这个过程会导致控件的渲染状态混乱,最终Rail无法正常显示在新视图中。

内容的提问来源于stack exchange,提问作者Mario Hernández

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 17:20:19