Skip to main content

Flet Terminal logo

Terminal

A GPU-accelerated native terminal control for Flet apps, powered by xterm.dart.

PyPI version Total users Python >= 3.14


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 DataChannel directly to the xterm.dart canvas, 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 an on_shortcut event and never leak bytes into your shell.
  • Responsive Mobile Wrapper & Zoom Controls: MobileTerminal includes zoom_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.component instances subscribed to an @ft.observable ModifierState — they repaint instantly on toggle, reset, or external state change.
  • Real Cursor Blink: A Dart-side Timer.periodic toggles cursorVisibleMode + notifyListeners() for true blink (upstream xterm 4.0.0 has no built-in blink).
  • 4 Built-in Themes: Dracula, JetBrains Dark, Matrix Green, and Colab 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 from flet_terminal.frozen_support) temporarily lifts Flet's _frozen flag 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

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)

Source distribution for flet-terminal 0.3.9
File Size Uploaded Provenance
flet_terminal-0.3.9.tar.gz 32.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for flet-terminal 0.3.9
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page