Instantly share code, notes, and snippets.
Created
August 1, 2022 18:21
-
Star
(4)
4
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save holvi-mikael/d235e06ea4cf8e3025b14e24e9af6468 to your computer and use it in GitHub Desktop.
Basic reactive menu layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import flet | |
from flet import AppBar | |
from flet import Card | |
from flet import Column | |
from flet import Container | |
from flet import Icon | |
from flet import IconButton | |
from flet import NavigationRail | |
from flet import NavigationRailDestination | |
from flet import Page | |
from flet import Row | |
from flet import Stack | |
from flet import Text | |
from flet import UserControl | |
from flet import VerticalDivider | |
from flet import colors | |
from flet import icons | |
class ResponsiveMenuLayout(Row): | |
def __init__(self, page, pages, *args, **kwargs): | |
super().__init__(*args, **kwargs) | |
self.expand = True | |
self.page = page | |
self.pages = pages | |
navigation_items = [navigation_item for navigation_item, _ in pages] | |
self.navigation_rail = self._build_navigation_rail(navigation_items) | |
page_contents = [page_content for _, page_content in pages] | |
self.menu_panel = Row( | |
controls=[self.navigation_rail, VerticalDivider(width=1)], | |
spacing=0, | |
) | |
self.content_area = Column(page_contents, expand=True) | |
self._was_portrait = self.is_portrait() | |
self._panel_visible = self.is_landscape() | |
self.set_navigation_content() | |
self._change_displayed_page() | |
self.page.on_resize = self.handle_resize | |
def select_page(self, page_number): | |
self.navigation_rail.selected_index = page_number | |
self._change_displayed_page() | |
def _navigation_change(self, e): | |
self._change_displayed_page() | |
def _change_displayed_page(self): | |
selected_index = self.navigation_rail.selected_index | |
# page_contents = [page_content for _, page_content in self.pages] | |
for i, content_page in enumerate(self.content_area.controls): | |
content_page.visible = selected_index == i | |
self.check_toggle_on_select() | |
self.page.update() | |
def _build_navigation_rail(self, navigation_items): | |
return NavigationRail( | |
selected_index=0, | |
label_type="all", | |
extended=True, | |
destinations=navigation_items, | |
on_change=self._navigation_change, | |
) | |
def handle_resize(self, e): | |
if self._was_portrait != self.is_portrait(): | |
self._was_portrait = self.is_portrait() | |
self._panel_visible = self.is_landscape() | |
self.set_navigation_content() | |
self.page.update() | |
def toggle_navigation(self): | |
self._panel_visible = not self._panel_visible | |
self.set_navigation_content() | |
self.page.update() | |
def check_toggle_on_select(self): | |
if self.is_portrait() and self._panel_visible: | |
self.toggle_navigation() | |
def set_navigation_content(self): | |
if self.is_landscape(): | |
self.add_landscape_content() | |
else: | |
self.add_portrait_content() | |
def add_landscape_content(self): | |
self.controls = [self.menu_panel, self.content_area] | |
self.menu_panel.visible = self._panel_visible | |
def add_portrait_content(self): | |
self.controls = [Stack(controls=[self.content_area, self.menu_panel], expand=True)] | |
self.menu_panel.visible = self._panel_visible | |
def is_portrait(self) -> bool: | |
# Return true if window/display is narrow | |
return self.page.window_height >= self.page.window_width | |
def is_landscape(self) -> bool: | |
# Return true if window/display is wide | |
return self.page.window_width > self.page.window_height | |
def main(page: Page, title="Basic Responsive Menu"): | |
page.title = title | |
menu_button = IconButton(icons.MENU) | |
page.appbar = AppBar( | |
leading=menu_button, | |
leading_width=40, | |
title=Text(title), | |
bgcolor=colors.SURFACE_VARIANT, | |
) | |
pages = [ | |
( | |
NavigationRailDestination( | |
icon=icons.LANDSCAPE_OUTLINED, selected_icon=icons.LANDSCAPE, label="Menu in landscape" | |
), | |
create_page( | |
"Menu in landscape", | |
"Menu in landscape is by default shown, side by side with the main content, but can be " | |
"hidden with the menu button.", | |
), | |
), | |
( | |
NavigationRailDestination( | |
icon=icons.PORTRAIT_OUTLINED, | |
selected_icon=icons.PORTRAIT, | |
label="Menu in portrait", | |
), | |
create_page( | |
"Menu in portrait", | |
"Menu in portrait is mainly expected to be used on a smaller mobile device. The menu is by default " | |
"hidden, and when shown with the menu button it is placed on top of the main content.", | |
), | |
), | |
] | |
menu_layout = ResponsiveMenuLayout(page, pages) | |
page.add(menu_layout) | |
menu_button.on_click = lambda e: menu_layout.toggle_navigation() | |
def create_page(title: str, body: str): | |
return Row( | |
controls=[ | |
Column( | |
horizontal_alignment="stretch", | |
controls=[ | |
Card(content=Container(Text(title, weight="bold"), padding=8)), | |
Text(body), | |
], | |
expand=True, | |
), | |
], | |
expand=True, | |
) | |
flet.app(target=main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment