You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi there,
I tried Flet for a few days and I really like it. As I'm on Linux / Gnome Shell with Wayland, the GTK white title bar annoyed me.
I decided to read the doc and to try to make a FramelessPage class that covers my needs.
What it does:
it adds a titlebar with minimize / maximize / close button
it adds handles to resize the app (I've got problem with the placement of thehandles on the corners, but it works)
it follows the desktop color scheme change
Here is the code:
"""A generic frameless page with custom title bar and resize handles.This module provides a reusable FramelessPage class that handles:- Custom frameless window with title bar- Window controls (minimize, maximize, close)- Edge and corner resize handles- Keyboard handling (ESC to close dialogs)Usage: from frameless_page import FramelessPage def main(page: ft.Page): page.theme_mode = ft.ThemeMode.DARK fp = FramelessPage(page, title="My App") fp.set_content(my_content) page.add(fp)"""importfletasftclassFramelessPage(ft.Column):
"""A generic frameless page with custom title bar and resize handles. This class creates a frameless window with: - A custom title bar with window controls - Edge and corner resize handles - ESC key handling to close dialogs Attributes: page: The Flet page instance. title: Window title displayed in title bar. """EDGE_MAP= {
"top": ft.WindowResizeEdge.TOP,
"bottom": ft.WindowResizeEdge.BOTTOM,
"left": ft.WindowResizeEdge.LEFT,
"right": ft.WindowResizeEdge.RIGHT,
"top_left": ft.WindowResizeEdge.TOP_LEFT,
"top_right": ft.WindowResizeEdge.TOP_RIGHT,
"bottom_left": ft.WindowResizeEdge.BOTTOM_LEFT,
"bottom_right": ft.WindowResizeEdge.BOTTOM_RIGHT,
}
CURSOR_MAP= {
"top": ft.MouseCursor.RESIZE_UP_DOWN,
"bottom": ft.MouseCursor.RESIZE_UP_DOWN,
"left": ft.MouseCursor.RESIZE_LEFT_RIGHT,
"right": ft.MouseCursor.RESIZE_LEFT_RIGHT,
"top_left": ft.MouseCursor.RESIZE_UP_LEFT,
"bottom_right": ft.MouseCursor.RESIZE_UP_LEFT,
"top_right": ft.MouseCursor.RESIZE_UP_RIGHT,
"bottom_left": ft.MouseCursor.RESIZE_UP_RIGHT,
}
def__init__(
self,
page: ft.Page,
title: str="App",
show_maximize: bool=True,
show_minimize: bool=True,
resize_handle_size: int=10,
**kwargs,
):
"""Initialize a frameless page. Args: page: The Flet page instance. title: Window title to display in title bar. show_maximize: Whether to show maximize/restore button. show_minimize: Whether to show minimize button. resize_handle_size: Size of resize handles in pixels. **kwargs: Additional arguments passed to ft.Column. """super().__init__(**kwargs)
self._page=pageself.resize_handle_size=resize_handle_sizeself._content_area=Noneself._is_fullscreen=Falseself._setup_window()
self._setup_keyboard()
self.title_bar=self._create_title_bar(title, show_maximize, show_minimize)
self.resize_handles=self._create_resize_handles()
self._build_layout()
def_setup_window(self) ->None:
"""Configure the window for frameless mode."""self._page.window.frameless=Trueself._page.window.title_bar_buttons_hidden=Trueself._page.window.resizable=Truedef_setup_keyboard(self) ->None:
"""Setup keyboard event handler for ESC key."""defon_keyboard(e: ft.KeyboardEvent):
ife.key=="Escape"andself._is_fullscreen:
self._page.pop_dialog()
self._is_fullscreen=Falseself._page.update()
self._page.on_keyboard_event=on_keyboarddef_create_title_bar(
self, title: str, show_maximize: bool, show_minimize: bool
) ->ft.WindowDragArea:
"""Create the custom title bar with window controls. Args: title: Window title text. show_maximize: Whether to show maximize button. show_minimize: Whether to show minimize button. Returns: WindowDragArea containing the title bar. """defminimize_click(e):
self._page.window.minimized=Trueself._page.update()
defmaximize_click(e):
self._page.window.maximized=notself._page.window.maximizedself._page.update()
asyncdefclose_click(e):
awaitself._page.window.close()
controls= []
ifshow_minimize:
controls.append(
ft.IconButton(
icon=ft.Icons.REMOVE,
icon_size=16,
on_click=minimize_click,
)
)
ifshow_maximize:
controls.append(
ft.IconButton(
icon=ft.Icons.CROP_SQUARE,
icon_size=16,
on_click=maximize_click,
)
)
controls.append(
ft.IconButton(
icon=ft.Icons.CLOSE,
icon_size=16,
on_click=close_click,
)
)
returnft.WindowDragArea(
content=ft.Container(
content=ft.Row(
[
ft.Text(title, weight=ft.FontWeight.BOLD),
ft.Row(controls, spacing=0),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True,
),
padding=10,
bgcolor=ft.Colors.SURFACE,
),
height=40,
maximizable=show_maximize,
)
def_create_resize_handle(self, edge: str) ->ft.GestureDetector:
"""Create a single resize handle for the specified edge. Args: edge: The resize edge ('top', 'bottom', 'left', 'right', 'top_left', 'top_right', 'bottom_left', 'bottom_right'). Returns: GestureDetector configured for the specified edge. """defon_drag(e):
self._page.run_task(self._page.window.start_resizing, self.EDGE_MAP[edge])
returnft.GestureDetector(
mouse_cursor=self.CURSOR_MAP[edge],
on_pan_start=on_drag,
)
def_create_resize_handles(self) ->list[ft.Container]:
"""Create all resize handles (edges and corners). Returns: List of Container widgets, each containing a GestureDetector. """size=self.resize_handle_sizehandles= {
"top_left": {"left": 0, "top": 0, "width": size, "height": size},
"top": {"left": size, "top": 0, "right": size, "height": size},
"top_right": {"right": 0, "top": 0, "width": size, "height": size},
"left": {
"left": 0,
"top": size,
"width": size,
"height": None,
"expand": True,
},
"right": {
"right": 0,
"top": size,
"width": size,
"height": None,
"expand": True,
},
"bottom_left": {"left": 0, "bottom": 0, "width": size, "height": size},
"bottom": {"left": size, "bottom": 0, "right": size, "height": size},
"bottom_right": {"right": 0, "bottom": 0, "width": size, "height": size},
}
containers= []
foredge, propsinhandles.items():
gd=self._create_resize_handle(edge)
containers.append(ft.Container(content=gd, **props))
returncontainersdef_build_layout(self) ->None:
"""Build the page layout with title bar and content area."""self._content_area=ft.Container(expand=True)
self.controls= [
self.title_bar,
ft.Stack(
controls=[
self._content_area,
*self.resize_handles,
],
expand=True,
),
]
self.expand=Truedefset_content(self, content: ft.Control) ->None:
"""Set the main content of the page. This replaces any existing content in the content area. Args: content: The control to display as main content. """ifself._content_areaisNone:
self._content_area=ft.Container(expand=True)
self._content_area.content=contentself._page.update()
defset_fullscreen(self, is_fullscreen: bool) ->None:
"""Update the fullscreen state (for ESC key handling). Args: is_fullscreen: Whether a dialog is currently displayed. """self._is_fullscreen=is_fullscreen
As I use flet for maybe 2 days, I'm not sure that I did things correctly. But, here is an example :
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi there,
I tried Flet for a few days and I really like it. As I'm on Linux / Gnome Shell with Wayland, the GTK white title bar annoyed me.
I decided to read the doc and to try to make a
FramelessPageclass that covers my needs.What it does:
Here is the code:
As I use flet for maybe 2 days, I'm not sure that I did things correctly. But, here is an example :
Capture.video.du.2026-03-05.10-18-01.mp4
If it helps... Tell me, I could create a package.
Beta Was this translation helpful? Give feedback.
All reactions