Terminal
A GPU-accelerated native terminal control for Flet apps, powered by xterm.dart.
A native, GPU-accelerated terminal control for Flet, built on top of xterm.dart.
flet-terminal provides high-performance VT100/ANSI terminal emulation across Windows, Linux, macOS, Android, and Web, utilizing low-latency binary DataChannel streaming to render thousands of lines per second without UI freezing.
Fully compatible with Flet 1.0's declarative component model (@ft.component, @ft.observable, use_state, use_effect) — including frozen-control safety via the built-in thaw() context manager.
Features
- High-Throughput Binary Streaming: Routes terminal data over Flet
DataChanneldirectly to thexterm.dartcanvas, bypassing string/MsgPack serialization overhead. - Cross-Platform Compatibility: Full feature parity across Desktop (
pty/winpty), Mobile (Android), and Web (WASM/Pyodide). - Declarative-First Design: Built for Flet 1.0's React-like component model. All internal mutations use
thaw()to safely update frozen controls inside declarative trees. - Leak-Free Host Shortcuts: Built-in combos (
Ctrl/Cmd+Shift+T/W/1-9/F/L/C/V, zoom,F1) are intercepted on the Dart side before the PTY — they fire anon_shortcutevent and never leak bytes into your shell. - Responsive Mobile Wrapper & Zoom Controls:
MobileTerminalincludeszoom_in(),zoom_out(),reset_zoom(), and a customizable virtual accessory keyboard (ESC,TAB,CTRL,ALT, arrows) with sticky modifier toggles and collapsible state. - Reactive CTRL/ALT Modifiers: Sticky modifier buttons are
@ft.componentinstances subscribed to an@ft.observableModifierState— they repaint instantly on toggle, reset, or external state change. - Real Cursor Blink: A Dart-side
Timer.periodictogglescursorVisibleMode+notifyListeners()for true blink (upstream xterm 4.0.0 has no built-in blink). - 4 Built-in Themes:
Dracula,JetBrains Dark,Matrix Green, andColab Light(Material light palette with orange cursor). Settings popup displays accurate checkmarks that update live. - Interactive Search & Selection: Built-in search bar with match counter, scrollback control, and clipboard integration (
select_all,copy_selection,paste,clear_selection). - Frozen-Control Safety: The
thaw()context manager (exported fromflet_terminal.frozen_support) temporarily lifts Flet's_frozenflag so imperative mutations work inside declarative trees — the same pattern Flet uses internally in_before_update_safe.
Installation
Install via pip:
pip install flet-terminal
Or using uv:
uv add flet-terminal
Quickstart
1. Declarative App (Recommended — Flet 1.0)
import flet as ft
from flet_terminal import MobileTerminal, BUILTIN_THEMES
@ft.component
def App():
page = ft.context.page
mt, _ = ft.use_state(lambda: MobileTerminal(
show_extra_keys=True,
show_search=False,
show_settings=True,
scrollback=10000,
font_family="JetBrains Mono",
font_size=13.0,
cursor_blink=True,
theme=BUILTIN_THEMES["JetBrains Dark"],
expand=True,
))
def on_bytes(payload: bytes):
# Echo back or forward to a PTY / remote shell
mt.send_bytes(payload)
ft.use_effect(lambda: mt.set_on_bytes(on_bytes), [])
return ft.Column(controls=[mt], spacing=0, expand=True)
def main(page: ft.Page):
page.title = "My Terminal"
page.theme_mode = ft.ThemeMode.DARK
page.padding = 0
page.render(App)
ft.run(main)
2. Basic Terminal (Terminal)
import flet as ft
from flet_terminal import Terminal, BUILTIN_THEMES
def main(page: ft.Page):
page.theme_mode = ft.ThemeMode.DARK
term = Terminal(
scrollback=10000,
font_family="JetBrains Mono",
font_size=13.0,
cursor_blink=True,
theme=BUILTIN_THEMES["Dracula"],
expand=True,
)
def on_terminal_input(data: bytes):
term.send_bytes(data)
term.set_on_bytes(on_terminal_input)
page.add(term)
term.write("\x1b[1;32mWelcome to FletTerminal!\x1b[0m\r\n> ")
ft.run(main)
3. Imperative Construction Inside Async Tasks
If you build MobileTerminal outside a component render (e.g. inside page.run_task), wrap construction in a throwaway renderer so the internal @ft.component buttons can instantiate:
from flet.components.component import Renderer
with Renderer().with_context():
mt = MobileTerminal(theme=BUILTIN_THEMES["JetBrains Dark"], expand=True)
Reactivity is unaffected — Component.update() creates its own renderer on every re-render.
API Reference
Terminal Properties
| Property | Type | Default | Description |
|---|---|---|---|
scrollback |
int |
10000 |
Maximum number of scrollback lines retained in the ring buffer. |
font_family |
str |
"JetBrains Mono" |
Monospace font family for rendering text. |
font_size |
float |
13.0 |
Font point size. |
cursor_style |
str |
"block" |
Cursor shape. Currently only "block" is supported (underline/bar removed due to upstream xterm 4.0.0 painter bug). |
cursor_blink |
bool |
True |
Whether the terminal cursor blinks (real Dart-side timer). |
theme |
dict |
None |
Dictionary mapping ANSI color keys to hex colors. |
read_only |
bool |
False |
When True, disables user keyboard input into the terminal canvas. |
auto_focus |
bool |
True |
Automatically focuses the terminal when mounted. |
Terminal Methods
| Method | Arguments | Description |
|---|---|---|
send_bytes(payload) |
bytes |
Sends binary data directly over the DataChannel to the terminal canvas. |
write(data) |
str | bytes |
Writes text or escape sequences to the terminal. |
clear() |
— | Clears the terminal scrollback and visible screen buffer. |
focus() |
— | Requests keyboard focus on the terminal control. |
search(query, start, direction) |
str, int, str |
Highlights and selects matching text in the scrollback buffer. |
select_all() |
— | Selects all text currently in the buffer. |
clear_selection() |
— | Clears any active selection. |
paste() |
— | Pastes clipboard content into the terminal. |
Terminal Events
| Event | Handler | Description |
|---|---|---|
on_data |
Callable[[ft.ControlEvent], None] |
Triggered when string-based text input occurs. |
on_resize |
Callable[[ft.ControlEvent], None] |
Fired when dimensions change. Event data contains JSON {"cols": int, "rows": int}. |
on_title_change |
Callable[[ft.ControlEvent], None] |
Triggered when OSC 0/2 title escape sequences are received. |
on_bell |
Callable[[ft.ControlEvent], None] |
Triggered when the bell character (\a / 0x07) is received. |
on_selection_change |
Callable[[ft.ControlEvent], None] |
Fired when selection or search matches update. |
on_shortcut |
Callable[[ShortcutEvent], None] |
Fired when a built-in host shortcut is pressed while the terminal has focus. The combo is consumed on the Dart side — it never reaches the PTY. e.shortcut carries the machine name. Active only when a handler is set. |
Built-in shortcuts (on_shortcut)
| Combination | e.shortcut value |
|---|---|
Ctrl/Cmd+Shift+T |
new_terminal |
Ctrl/Cmd+Shift+W |
close_terminal |
Ctrl/Cmd+Shift+1 .. 9 |
switch_terminal_1 .. switch_terminal_9 |
Ctrl/Cmd+Shift+F |
toggle_search |
Ctrl/Cmd+Shift+L |
clear |
Ctrl/Cmd+Shift+C |
copy |
Ctrl/Cmd+Shift+V |
paste |
Ctrl/Cmd+Shift+= |
zoom_in |
Ctrl/Cmd+Shift+- |
zoom_out |
Ctrl/Cmd+Shift+0 |
zoom_reset |
Ctrl/Cmd+PageUp / PageDown |
prev_terminal / next_terminal |
F1 |
help |
MobileTerminal Additional Methods & Properties
| Method / Property | Description |
|---|---|
set_theme(name) |
Switch to a built-in theme by name. |
set_cursor_style(style) |
Set cursor style (currently "block" only). |
toggle_cursor_blink() |
Toggle cursor blink on/off. |
zoom_in(step=1.0) |
Increase font size. |
zoom_out(step=1.0) |
Decrease font size. |
reset_zoom() |
Reset to default font size. |
toggle_search() |
Show/hide the built-in search bar. |
copy_selection() |
Copy selected text to clipboard. |
paste() |
Paste clipboard into terminal. |
select_all() / clear_selection() |
Selection helpers. |
send_bytes(payload) / write(data) / clear() / focus() |
Inherited from Terminal. |
set_on_bytes(handler) |
Register the input handler. |
| Read-only properties | |
font_size |
Current font size. |
cursor_blink |
Current blink state. |
cursor_style |
Current cursor style. |
theme_name |
Active theme name (or None). |
show_search |
Whether search bar is visible (settable). |
ctrl_active / alt_active |
Sticky modifier state (settable). |
ExtraKeysBar Constructor
| Parameter | Type | Default | Description |
|---|---|---|---|
on_send_payload |
Callable[[bytes], None] |
required | Called with the final byte payload after modifier processing. |
on_modifier_change |
Callable[[bool, bool], None] |
required | Called with (ctrl, alt) when sticky state changes. |
show_settings |
bool |
True |
Show the gear icon settings popup. |
on_set_theme |
Callable[[str], None] |
None |
Theme preset callback. |
on_set_cursor |
Callable[[str], None] |
None |
Cursor style callback. |
on_toggle_blink |
Callable[[], None] |
None |
Blink toggle callback. |
on_toggle_search |
Callable[[], None] |
None |
Search toggle callback. |
on_copy / on_paste / on_select_all / on_clear |
Callable[[], None] |
None |
Clipboard/buffer callbacks. |
keys |
list[tuple[str, bytes | None]] |
DEFAULT_EXTRA_KEYS |
Custom key layout. None payload = modifier key. |
Built-in Themes
from flet_terminal import BUILTIN_THEMES, get_theme
# Available: "Dracula", "JetBrains Dark", "Matrix Green", "Colab Light"
my_theme = get_theme("Dracula")
| Theme | Background | Cursor | Best for |
|---|---|---|---|
| Dracula | #1E1F29 |
#FF79C6 (pink) |
Dark mode default |
| JetBrains Dark | #1E1E2E |
#F5E0DC (warm white) |
Dark mode (Catppuccin-inspired) |
| Matrix Green | #0D1117 |
#00FF66 (green) |
Retro / hacker aesthetic |
| Colab Light | #FFFFFF |
#F97316 (orange) |
Light mode / follows app theme |
Desktop Demo App
Try the standalone Flet Terminal demo application directly on your OS:
| Platform | Download | Notes |
|---|---|---|
| 🪟 Windows (x64) | FletTerminal_windows_x64.zip | Portable Windows executable (.exe) |
| 🐧 Linux (x86_64) | FletTerminal_linux_x86_64.tar.gz | Universal Linux tarball (tar -xzf) |
| 📦 All Releases | View Releases Page | Changelog and release notes |
Frozen-Control Support
Flet 1.0's declarative renderer stamps _frozen = True on all component-rendered controls. Imperative mutations (.update(), property assignment) raise RuntimeError: Frozen controls cannot be updated.
flet_terminal handles this internally via thaw():
from flet_terminal.frozen_support import thaw
with thaw(some_control):
some_control.style = new_style
some_control.update()
All MobileTerminal setters (set_theme, zoom_in, toggle_search, etc.) and ExtraKeysBar mutations already use thaw() internally — no extra wrapping needed by consumers.
License
This project is licensed under the MIT License. See LICENSE for details.
Metadata
Release files for flet-terminal 0.3.9
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| flet_terminal-0.3.9.tar.gz | 32.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flet_terminal-0.3.9-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 65.9 kB
Release files / flet_terminal-0.3.9.tar.gz
| Download URL | flet_terminal-0.3.9.tar.gz |
|---|---|
| Size | 32.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
47c8fa069dff87872967e24db017e7a44e55ff8e40c26f1196195341babdbb66
|
|
BLAKE2b-256 checksum How to use checksums |
daccdd120643dd1ed44e30586cf27348bf23f61676fc77ac1e40604cea03837c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / flet_terminal-0.3.9-py3-none-any.whl
| Download URL | flet_terminal-0.3.9-py3-none-any.whl |
|---|---|
| Size | 33.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4d5206d2bd403ef0b653d25d1b83ec32774e812c02e6fdf54deb59e4847c7a2d
|
|
BLAKE2b-256 checksum How to use checksums |
a6149c47e905abbd8af2f9709bbe052dfccc0cf5effae4d662ac17a6563aaa81
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logSecurity for flet-terminal 0.3.9
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for flet_terminal-0.3.9.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.