From 5ac9799b5d663f73c98edade5198b06c0abf8d66 Mon Sep 17 00:00:00 2001 From: Flook Date: Thu, 7 May 2026 04:59:47 +0700 Subject: [PATCH] feat: implement adaptive layout and sync navigation state --- app/layout.py | 28 +++++++++++++++++++++------- app/widgets/adaptive_menu.py | 30 +++++++++++++----------------- main.py | 24 +++++++++++++++++++++++- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/app/layout.py b/app/layout.py index 4dbb1ac..e5855c6 100644 --- a/app/layout.py +++ b/app/layout.py @@ -1,17 +1,31 @@ import flet as ft -from app.widgets.adaptive_menu import adaptive_menu +from app.widgets.adaptive_menu import adaptive_menu, get_selected_index_from_route + +MOBILE_BREAKPOINT = 600 + +def get_layout_mode(page: ft.Page): + width = page.width if page.width and page.width > 0 else 1024 + return "mobile" if width < MOBILE_BREAKPOINT else "desktop" def main_layout(page: ft.Page, content_container: ft.Container): print(">>> ENTER main_layout") - menu = adaptive_menu(page) - # ตรวจสอบโหมด (ป้องกันหน้าจอขาวจาก width=0) - width = page.width if page.width > 0 else 1024 # default ไว้ที่ desktop ก่อนถ้ายังไม่รู้ width - is_mobile = width < 600 - print(f">>> Device mode: {'MOBILE' if is_mobile else 'DESKTOP'} (Width: {page.width})") + mode = get_layout_mode(page) - if is_mobile: + # เก็บ mode instance ไว้ที่ page + page._layout_mode = mode + + selected_index = get_selected_index_from_route(page.route) + + menu = adaptive_menu(page, selected_index) + + # เก็บ menu instance ไว้ที่ page + page._menu_control = menu + + print(f">>> Device mode: {mode}") + + if mode == "mobile": return ft.Column( controls=[ content_container, diff --git a/app/widgets/adaptive_menu.py b/app/widgets/adaptive_menu.py index b72cfa4..1cf8ff6 100644 --- a/app/widgets/adaptive_menu.py +++ b/app/widgets/adaptive_menu.py @@ -1,13 +1,19 @@ import flet as ft -def adaptive_menu(page: ft.Page): - is_mobile = page.width < 600 +MENU_KEYS = ['articles', 'courses', 'my_courses', 'progress', 'profile', 'logout'] - menu_keys = ['articles', 'courses', 'my_courses', 'progress', 'profile', 'logout'] +def get_selected_index_from_route(route: str): + key = route.replace("/", "").split("/")[0] + if key in MENU_KEYS: + return MENU_KEYS.index(key) + return 0 + +def adaptive_menu(page: ft.Page, selected_index: int): + is_mobile = page.width < 600 async def handle_change(e): index = int(e.control.selected_index) - key = menu_keys[index] + key = MENU_KEYS[index] if key == "logout": from app.state import state @@ -15,17 +21,7 @@ def adaptive_menu(page: ft.Page): await page.push_route("/login") # หรือ page.go return - route = f"/{menu_keys[index]}" - print(f">>> Navigating to: {route}") - await page.push_route(route) - - # sync selected index กับ route - current_route = page.route.replace("/", "") or "articles" - - if current_route not in menu_keys: - current_route = "articles" - - current_selected_index = menu_keys.index(current_route) + await page.push_route(f"/{key}") if is_mobile: return ft.NavigationBar( @@ -38,7 +34,7 @@ def adaptive_menu(page: ft.Page): ft.NavigationBarDestination(icon=ft.Icons.LOGOUT, label="ออกจากระบบ" ), ], - selected_index=current_selected_index, + selected_index=selected_index, on_change=handle_change, ) else: @@ -52,7 +48,7 @@ def adaptive_menu(page: ft.Page): ft.NavigationRailDestination(icon=ft.Icons.LOGOUT, label="Logout" ), ], - selected_index=current_selected_index, + selected_index=selected_index, on_change=handle_change, extended=True, ) \ No newline at end of file diff --git a/main.py b/main.py index 67f1610..279d0ae 100644 --- a/main.py +++ b/main.py @@ -9,8 +9,11 @@ from app.pages.my_courses import my_courses_page from app.pages.progress import progress_page from app.pages.profile import profile_page from app.state import state +from app.widgets.adaptive_menu import get_selected_index_from_route + async def main(page: ft.Page): + page.title = "LMS Frontend" # register font @@ -23,7 +26,7 @@ async def main(page: ft.Page): page.theme = ft.Theme(font_family="NotoSansThai") # GLOBAL CONTAINER (รองรับ Persistent Layout + Dynamic Content) - main_container = ft.Container(expand=True) + main_container = ft.Container(expand=True, padding=10) # ROUTES MAP routes = { @@ -88,6 +91,10 @@ async def main(page: ft.Page): else: main_container.content = ft.Text("404 NOT FOUND") + # หลังจาก set main_container.content แล้ว ให้อับเดตเมนูที่เลือก รองรับกรณี Back/Forward + if hasattr(page, "_menu_control"): + page._menu_control.selected_index = get_selected_index_from_route(page.route) + page.update() # LOGIN SUCCESS @@ -97,6 +104,21 @@ async def main(page: ft.Page): page.on_route_change = route_change + def on_resize(e): + width = page.width if page.width and page.width > 0 else 1024 + new_mode = "mobile" if width < 600 else "desktop" + + if getattr(page, "_layout_mode", None) == new_mode: + return + + print(f">>> Layout mode changed: {page._layout_mode} -> {new_mode}") + + if page.views and page.views[-1].route == "/main": + page.views[-1].controls[0] = main_layout(page, main_container) + page.update() + + page.on_resize = on_resize + # start app await page.push_route("/login")