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

如何在Flet中实现多页面切换?附现有代码求助

解决方案:Flet多页面切换与组件复用

先修正原代码里的两处小问题:

  • CustomAppBar中self.appbar_items未定义,需把appbar_items设为实例变量才能在登录后修改菜单
  • self.store没有实现,临时用列表模拟用户存储功能

以下是完整的实现代码,包含3个可切换的页面,每个页面都复用自定义的AppBar和NavigationDrawer:

import flet as ft
from flet import (
    RouteChangeEvent, View, AppBar, PopupMenuItem, PopupMenuButton,
    Icon, icons, Text, colors, Container, margin, TextField, AlertDialog,
    ElevatedButton, Column
)

class CustomNavigationDrawer(ft.NavigationDrawer):
    def __init__(self, page):
        super().__init__()
        self.page = page
        # 初始化抽屉选项,每个选项对应一个路由
        self.controls = [
            ft.Container(height=12),
            ft.NavigationDrawerDestination(
                label="页面1",
                icon=ft.icons.DOOR_BACK_DOOR_OUTLINED,
                selected_icon_content=ft.Icon(ft.icons.DOOR_BACK_DOOR),
                on_click=lambda e: self.switch_route("/page1")
            ),
            ft.Divider(thickness=2),
            ft.NavigationDrawerDestination(
                icon_content=ft.Icon(ft.icons.MAIL_OUTLINED),
                label="页面2",
                selected_icon=ft.icons.MAIL,
                on_click=lambda e: self.switch_route("/page2")
            ),
            ft.NavigationDrawerDestination(
                icon_content=ft.Icon(ft.icons.PHONE_OUTLINED),
                label="页面3",
                selected_icon=ft.icons.PHONE,
                on_click=lambda e: self.switch_route("/page3")
            ),
        ]

    def switch_route(self, route):
        # 切换路由并关闭抽屉
        self.page.route = route
        self.open = False
        self.page.update()

    def add_custom_control(self, control):
        self.controls.append(control)

class User:
    def __init__(self, name, password):
        self.name = name
        self.password = password

class CustomAppBar(AppBar):
    def __init__(self, title_text: str, page):
        self.page = page
        self.users = []  # 模拟用户存储
        login_profile_button = PopupMenuItem(text="Log in", on_click=self.login)
        self.appbar_items = [
            login_profile_button,
            PopupMenuItem(),  # divider
            PopupMenuItem(text="Settings"),
        ]

        super().__init__(
            leading=Icon(None),
            leading_width=-1,
            title=Text(title_text, font_family="Pacifico", size=32, text_align="start"),
            center_title=False,
            toolbar_height=75,
            bgcolor=colors.LIGHT_BLUE_ACCENT_700,
            actions=[
                Container(
                    content=PopupMenuButton(items=self.appbar_items, tooltip="Menu rápido"),
                    margin=margin.only(left=50, right=25),
                )
            ],
        )

    def login(self, e):
        def close_dlg(e):
            if user_name.value == "" or password.value == "":
                user_name.error_text = "请输入用户名"
                password.error_text = "请输入密码"
                self.page.update()
                return
            
            user = User(user_name.value, password.value)
            # 检查用户是否已存在
            existing_user = next((u for u in self.users if u.name == user.name), None)
            if not existing_user:
                self.users.append(user)
            
            self.page.client_storage.set("current_user", user.name)
            dialog.open = False
            # 更新登录按钮为用户信息
            self.appbar_items[0] = PopupMenuItem(text=f"{user.name}'s Profile")
            self.page.update()

        user_name = TextField(label="Usuário")
        password = TextField(label="Password", password=True)
        dialog = AlertDialog(
            title=Text("Identifique-se"),
            content=Column(
                [user_name, password, ElevatedButton(text="Login", on_click=close_dlg)],
                tight=True,
            ),
            on_dismiss=lambda e: print("Modal dialog dismissed!"),
        )
        self.page.dialog = dialog
        dialog.open = True
        self.page.update()        

    def add_appbar_item(self, item):
        self.appbar_items.append(item)

def main(page: ft.Page):
    page.title = 'RD STATION'
    page.theme_mode = "dark"

    # 定义页面生成函数,复用AppBar和Drawer
    def get_page_view(title, route):
        appbar = CustomAppBar(title, page)
        appbar.add_appbar_item(PopupMenuItem(text="New Item"))
        drawer = CustomNavigationDrawer(page)
        # 添加自定义抽屉选项
        drawer.add_custom_control(ft.NavigationDrawerDestination(
            icon_content=ft.Icon(ft.icons.SETTINGS_OUTLINED),
            label="Configurações",
            selected_icon=ft.icons.SETTINGS,
            on_click=lambda e: print("设置页面待实现")
        ))
        
        # 页面主体内容,每个页面不同
        if route == "/page1":
            content = Text("这是页面1的内容", size=24)
        elif route == "/page2":
            content = Column([Text("页面2内容", size=24), ElevatedButton(text="页面2按钮")])
        elif route == "/page3":
            content = Container(
                content=Text("页面3内容", size=24),
                bgcolor=colors.BLUE_GREY_100,
                padding=20,
                border_radius=10
            )
        else:
            content = Text("默认页面", size=24)
        
        return View(
            route,
            [
                ft.IconButton(
                    icon=ft.icons.MENU_SHARP,
                    icon_color="blue400",
                    icon_size=25,
                    tooltip="Clique para abrir",
                    on_click=lambda e: (setattr(drawer, "open", True), page.update())
                ),
                content
            ],
            appbar=appbar,
            drawer=drawer
        )

    # 路由切换处理函数
    def on_route_change(e: RouteChangeEvent):
        page.views.clear()
        # 根据当前路由加载对应页面
        if e.route == "/page1":
            page.views.append(get_page_view("页面1", "/page1"))
        elif e.route == "/page2":
            page.views.append(get_page_view("页面2", "/page2"))
        elif e.route == "/page3":
            page.views.append(get_page_view("页面3", "/page3"))
        else:
            # 默认跳转到页面1
            page.route = "/page1"
            page.views.append(get_page_view("页面1", "/page1"))
        page.update()

    # 监听路由变化
    page.on_route_change = on_route_change
    # 初始化路由
    page.go("/page1")

ft.app(main)

关键实现说明:

  1. 组件复用优化:

    • CustomNavigationDrawer初始化时传入page实例,绑定每个选项的点击事件,点击后直接切换路由并关闭抽屉
    • CustomAppBar也传入page实例,修复了登录后菜单更新的逻辑,用列表模拟用户存储
  2. 页面切换逻辑:

    • 使用Flet的View类定义每个页面,每个View包含独立的AppBar、Drawer和主体内容
    • 通过on_route_change事件监听路由变化,根据路由加载对应的View
    • 初始化时默认跳转到/page1
  3. 抽屉与菜单功能:

    • 每个页面的抽屉都保留了原有的选项,还可以通过add_custom_control添加自定义选项
    • AppBar的登录功能修复了原代码的错误,登录后会更新菜单显示用户信息

内容的提问来源于stack exchange,提问作者Elias Coutinho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 15:02:32