Skip to Content
Flet🧭 Navigation & Routing

Navigation & Routing trong Flet

Học cách điều hướng giữa các màn hình trong ứng dụng Flet. ⏱️ 20 phút

🎯 Mục tiêu

Nắm vững:

  • Routes và Navigation
  • Views (màn hình)
  • AppBar và NavigationBar
  • Passing data giữa các màn hình

📍 Routes cơ bản

Flet sử dụng routes để điều hướng, tương tự web URLs:

import flet as ft @ft.observable class RouterState: route: str = "/" state = RouterState() @ft.component def HomeView(): return ft.View( "/", [ ft.AppBar(title=ft.Text("Home")), ft.Text("Welcome to Home!", size=24), ft.ElevatedButton( "Go to Settings", on_click=lambda _: setattr(state, 'route', '/settings'), ), ], ) @ft.component def SettingsView(): return ft.View( "/settings", [ ft.AppBar(title=ft.Text("Settings")), ft.Text("Settings Page", size=24), ft.ElevatedButton( "Back to Home", on_click=lambda _: setattr(state, 'route', '/'), ), ], ) @ft.component def Router(): if state.route == "/settings": return SettingsView() return HomeView() def main(page: ft.Page): page.title = "Navigation Demo" page.add(Router()) ft.run(main)

🏗️ Tổ chức code với Components

Tách view thành các components riêng:

import flet as ft @ft.observable class AppState: current_route: str = "/" def go(self, route: str): self.current_route = route state = AppState() @ft.component def HomeView(): return ft.View( "/", [ ft.AppBar(title=ft.Text("Home"), bgcolor=ft.Colors.BLUE), ft.Container( content=ft.Column([ ft.Text("🏠 Home Page", size=28, weight=ft.FontWeight.BOLD), ft.ElevatedButton("Go to Profile", on_click=lambda _: state.go("/profile")), ft.ElevatedButton("Go to Settings", on_click=lambda _: state.go("/settings")), ], spacing=20), padding=30, ), ], ) @ft.component def ProfileView(): return ft.View( "/profile", [ ft.AppBar( title=ft.Text("Profile"), bgcolor=ft.Colors.GREEN, leading=ft.IconButton(ft.Icons.ARROW_BACK, on_click=lambda _: state.go("/")), ), ft.Container( content=ft.Column([ ft.Text("👤 Profile Page", size=28, weight=ft.FontWeight.BOLD), ft.Text("User: John Doe"), ], spacing=20), padding=30, ), ], ) @ft.component def SettingsView(): return ft.View( "/settings", [ ft.AppBar( title=ft.Text("Settings"), bgcolor=ft.Colors.ORANGE, leading=ft.IconButton(ft.Icons.ARROW_BACK, on_click=lambda _: state.go("/")), ), ft.Container( content=ft.Column([ ft.Text("⚙️ Settings Page", size=28, weight=ft.FontWeight.BOLD), ft.Switch(label="Dark Mode"), ft.Switch(label="Notifications"), ], spacing=20), padding=30, ), ], ) @ft.component def AppRouter(): routes = { "/": HomeView, "/profile": ProfileView, "/settings": SettingsView, } ViewComponent = routes.get(state.current_route, HomeView) return ViewComponent() def main(page: ft.Page): page.title = "Navigation Demo" page.add(AppRouter()) ft.run(main)

🔗 Route với Parameters

Truyền dữ liệu qua state:

import flet as ft @ft.observable class ItemsState: items: list = [ {"id": 1, "name": "Item 1", "description": "Description 1"}, {"id": 2, "name": "Item 2", "description": "Description 2"}, {"id": 3, "name": "Item 3", "description": "Description 3"}, ] current_route: str = "/" selected_item_id: int = None def go_to_detail(self, item_id: int): self.selected_item_id = item_id self.current_route = "/detail" def go_back(self): self.selected_item_id = None self.current_route = "/" def get_selected_item(self): return next((i for i in self.items if i["id"] == self.selected_item_id), None) state = ItemsState() @ft.component def ItemsList(): return ft.View( "/", [ ft.AppBar(title=ft.Text("Items")), ft.Column([ ft.ListTile( title=ft.Text(item["name"]), subtitle=ft.Text(item["description"]), on_click=lambda _, id=item["id"]: state.go_to_detail(id), trailing=ft.Icon(ft.Icons.ARROW_FORWARD_IOS), ) for item in state.items ]), ], ) @ft.component def ItemDetail(): item = state.get_selected_item() if not item: return ft.View("/detail", [ft.Text("Item not found")]) return ft.View( "/detail", [ ft.AppBar( title=ft.Text("Item Detail"), leading=ft.IconButton(ft.Icons.ARROW_BACK, on_click=lambda _: state.go_back()), ), ft.Container( content=ft.Column([ ft.Text(f"ID: {item['id']}", size=16), ft.Text(item["name"], size=28, weight=ft.FontWeight.BOLD), ft.Text(item["description"], size=18), ], spacing=10), padding=30, ), ], ) @ft.component def App(): if state.current_route == "/detail": return ItemDetail() return ItemsList() def main(page: ft.Page): page.title = "Route Params" page.add(App()) ft.run(main)

🧭 NavigationBar (Bottom Navigation)

import flet as ft @ft.observable class NavState: selected_index: int = 0 def set_index(self, index: int): self.selected_index = index state = NavState() @ft.component def ContentArea(): contents = ["🏠 Home Content", "🔍 Search Content", "👤 Profile Content"] return ft.Container( content=ft.Text(contents[state.selected_index], size=24), expand=True, alignment=ft.alignment.center, ) @ft.component def BottomNavApp(): return ft.Column([ ContentArea(), ft.NavigationBar( selected_index=state.selected_index, destinations=[ ft.NavigationBarDestination(icon=ft.Icons.HOME, label="Home"), ft.NavigationBarDestination(icon=ft.Icons.SEARCH, label="Search"), ft.NavigationBarDestination(icon=ft.Icons.PERSON, label="Profile"), ], on_change=lambda e: state.set_index(e.control.selected_index), ), ], expand=True) def main(page: ft.Page): page.title = "Bottom Navigation" page.add(BottomNavApp()) ft.run(main)

🚂 NavigationRail (Side Navigation)

import flet as ft @ft.observable class RailState: selected_index: int = 0 state = RailState() @ft.component def ContentPanel(): labels = ["Home", "Settings", "About"] return ft.Container( content=ft.Text(labels[state.selected_index], size=24), expand=True, alignment=ft.alignment.center, ) @ft.component def SideNavApp(): return ft.Row([ ft.NavigationRail( selected_index=state.selected_index, label_type=ft.NavigationRailLabelType.ALL, destinations=[ ft.NavigationRailDestination(icon=ft.Icons.HOME, label="Home"), ft.NavigationRailDestination(icon=ft.Icons.SETTINGS, label="Settings"), ft.NavigationRailDestination(icon=ft.Icons.INFO, label="About"), ], on_change=lambda e: setattr(state, 'selected_index', e.control.selected_index), ), ft.VerticalDivider(width=1), ContentPanel(), ], expand=True) def main(page: ft.Page): page.title = "Navigation Rail" page.add(SideNavApp()) ft.run(main)

📱 Tabs Navigation

import flet as ft @ft.observable class TabsState: selected_index: int = 0 state = TabsState() @ft.component def TabContent(text: str, icon): return ft.Container( content=ft.Column([ ft.Icon(icon, size=48, color=ft.Colors.PRIMARY), ft.Text(text, size=20), ], horizontal_alignment=ft.CrossAxisAlignment.CENTER, spacing=10), padding=40, alignment=ft.alignment.center, ) @ft.component def TabsApp(): tabs_data = [ ("Home", ft.Icons.HOME, "Welcome to Home Tab"), ("Settings", ft.Icons.SETTINGS, "Configure your settings"), ("About", ft.Icons.INFO, "About this app"), ] return ft.Tabs( selected_index=state.selected_index, on_change=lambda e: setattr(state, 'selected_index', e.control.selected_index), tabs=[ ft.Tab( text=name, icon=icon, content=TabContent(content, icon), ) for name, icon, content in tabs_data ], expand=True, ) def main(page: ft.Page): page.title = "Tabs Demo" page.add(TabsApp()) ft.run(main)

📋 Tổng kết Navigation Types

TypeUse Case
Custom Router + StateMulti-page apps, deep linking
NavigationBarMobile bottom nav
NavigationRailDesktop side nav
TabsContent sections

⏭️ Tiếp theo

Tuyệt! Tiếp tục với Theming để học tùy chỉnh giao diện.

Last updated on