Skip to main content

flet-code-editor-dsl

A code editor control for Flet with regex-based syntax highlighting, optional line numbers, toggleable colors and configurable scrolling.

Python Flet License


Table of Contents

  1. Overview
  2. Features
  3. Requirements
  4. Installation
  5. Quick Start
  6. API Reference
  7. Line Numbers
  8. Enabling and Disabling Colors
  9. Scrolling and Size Modes
  10. Controlling the Editor at Runtime
  11. Complete Example
  12. Important: First Build Required
  13. Building Your App
  14. Project Structure
  15. Known Limitations
  16. Troubleshooting
  17. Contributing
  18. License

Overview

flet-code-editor-dsl is a Flet extension that embeds code editors for Flutter. You describe how text is colored with a list of HighlightRule objects, each one a regular expression paired with a color. Two controls are provided:

Control Built on Use it when
FletCodeEditor (recommended) re_editor, a custom editor that lays out and paints each line independently and builds only the visible lines. Highlighting uses a hand-written tokenizer (no regex), configured with Language. You edit large scripts, or typing must stay fast.
FletCodeField Flutter TextField (code_text_field) with per-line highlight caching Small texts, or you want the platform's native text field behavior.

Features

  • Native Flutter rendering through the Flet extension mechanism.
  • Regex-based highlighting rules defined from Python.
  • Line numbers that can be shown or hidden.
  • Colors that can be enabled or disabled without losing your rules.
  • Scroll modes: fixed size, bounded growth, or unlimited growth, plus horizontal scrolling or line wrapping.
  • on_change event for reading the current text.
  • Inherits standard layout properties from ft.LayoutControl (width, height, expand, margin, etc.).

Requirements

Component Version
Python >= 3.10
Flet >= 0.85.2
Dart SDK >= 3.3.0 < 4.0.0
Flutter A version compatible with Flet 0.85

Note: Because this package contains a Flutter extension, your app must declare it in pyproject.toml and be built once with flet build. See Important: First Build Required.


Installation

From PyPI

pip install flet-code-editor-dsl

Declare it in your app's pyproject.toml:

[project]
name = "my-flet-app"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
  "flet",
  "flet-code-editor-dsl",
]

From GitHub

pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git"
dependencies = [
  "flet",
  "flet-code-editor-dsl @ git+https://github.com/obgwew/flet-code-editor-dsl.git",
]

Append @<branch|tag|commit> to the URL to pin a version.

Local editable install

git clone https://github.com/obgwew/flet-code-editor-dsl.git
cd flet-code-editor-dsl
pip install -e .

Verifying the installation

python -c "import flet_code_editor_dsl as m; print(m.__all__)"

Expected output:

['FletCodeEditor', 'FletCodeField', 'HighlightRule', 'VerticalMode']

Quick Start

import flet as ft
from flet_code_editor_dsl import FletCodeField, HighlightRule


def main(page: ft.Page):
    editor = FletCodeField(
        value="let x = 42",
        rules=[
            HighlightRule(pattern=r"\b(let|if|else)\b", color="#C586C0"),
            HighlightRule(pattern=r"\d+", color="#B5CEA8"),
        ],
        height=300,
    )
    page.add(editor)


ft.run(main)

FletCodeEditor

import flet as ft
from flet_code_editor_dsl import (
    FletCodeEditor, Language, WordGroup, StringRule, BlockComment, Sigil, TokenColors,
)

LANG = Language(
    words=[WordGroup(["let", "if", "else", "while", "return"], "#C586C0")],
    line_comments=["#"],
    block_comments=[BlockComment("/*", "*/")],
    strings=[StringRule('"'), StringRule("'")],
    sigils=[Sigil("$", "#9CDCFE")],
    colors=TokenColors(
        comment="#6A9955", string="#CE9178", escape="#D7BA7D",
        number="#B5CEA8", function="#DCDCAA", operator="#D4D4D4",
    ),
)


def main(page: ft.Page):
    page.add(
        FletCodeEditor(
            value='let x = 42  /* note */\nprint("hi\\n", $name)',
            language=LANG.to_json(),
            default_color="#D4D4D4",
            background_color="#1E1E1E",
            height=300,
        )
    )


ft.run(main)

API Reference

from flet_code_editor_dsl import FletCodeField, HighlightRule, VerticalMode

HighlightRule

Field Type Description
pattern str Regular expression selecting the text to color. Uses the Dart RegExp engine.
color str #RRGGBB or #AARRGGBB.

HighlightRule is used by FletCodeField only. FletCodeEditor uses a tokenizer described by Language instead of regex rules.

FletCodeEditor

Property Type Default Description
value str "" The text. Setting it from Python replaces the editor content.
language str "" Tokenizer configuration: Language(...).to_json(). Empty = no highlighting.
line_numbers bool True Show line numbers.
syntax_highlighting bool True False disables all colors (rules are kept).
wrap bool False False: horizontal scrolling. True: long lines wrap.
read_only bool False Disable editing.
vertical_mode VerticalMode FIXED FIXED, BOUNDED or UNLIMITED (see below).
min_lines / max_lines int | None None Limits for BOUNDED (fall back to 1 and 10).
font_family str "Consolas" Editor font.
font_size float 12.0 Font size.
default_color str | None None Color of text not matched by any rule (library default if None).
background_color str | None None Editor background (library default if None).
debounce_ms int 150 Delay before the text is sent back to Python and on_change fires. Keeps typing fast in large documents. value on the Python side can lag by this delay.
show_errors bool True Show error underlines (needs Language(errors=ErrorRules(...))).
code_folding bool True Show fold markers (needs Language(folds=[...]) or fold_by_indent).
width_level / height_level int | None 13 Visible size of the editor box, 1 to 30. See Visible size.
on_change handler None Called with the new text in e.data (after debounce_ms).

Vertical modes for FletCodeEditor: FIXED fills the given height (set height, or expand=True inside a bounded parent) and scrolls inside. BOUNDED and UNLIMITED size the editor from the number of lines (wrapped lines are not counted), between min_lines and max_lines or without limit. The height is computed from the font metrics, so it can be off by a few pixels.

FletCodeField

Property Type Default Description
value str "" The text shown in the editor.
rules list[HighlightRule] [] Regex highlighting rules (single-line).
line_numbers bool True Show a line-number gutter.
syntax_highlighting bool True When False, rules are ignored and no colors are applied.
incremental_highlighting bool True Highlight line by line and re-highlight only the edited line. Set False to highlight the whole text at once (needed for patterns that span several lines). See Performance.
wrap bool False False: long lines scroll horizontally. True: long lines wrap.
vertical_mode VerticalMode FIXED FIXED, BOUNDED or UNLIMITED. See Scrolling and Size Modes.
min_lines int | None None Minimum visible lines in BOUNDED mode (falls back to 1).
max_lines int | None None Maximum visible lines in BOUNDED mode (falls back to 10).
on_change ft.ControlEventHandler | None None Called whenever the text changes.

All ft.LayoutControl properties (width, height, expand, margin, visible, opacity, ...) are supported.

Color format

Format Example Meaning
#RRGGBB #FF8800 Opaque color (alpha FF is added automatically).
#AARRGGBB #80FF8800 Color with an explicit alpha channel.

Line Numbers

FletCodeField(line_numbers=True)    # default: gutter with line numbers
FletCodeField(line_numbers=False)   # no gutter

Enabling and Disabling Colors

syntax_highlighting switches coloring on or off while keeping your rules, so you can toggle it back at any time.

editor = FletCodeField(rules=[...], syntax_highlighting=False)   # plain text

editor.syntax_highlighting = True
editor.update()

Scrolling and Size Modes

Vertical behavior is controlled by vertical_mode; horizontal behavior by wrap.

vertical_mode Behavior
FIXED ("fixed") The editor fills the height it is given (height=..., or expand=True inside a Column) and scrolls internally.
BOUNDED ("bounded") The editor grows with the text between min_lines and max_lines, then scrolls.
UNLIMITED ("unlimited") The editor grows with the text without limit. It does not scroll by itself, so place it inside a scrollable parent.
wrap Behavior
False Long lines are not wrapped; the editor scrolls horizontally.
True Long lines wrap to the next visual line.
# Fixed height with internal scrolling
FletCodeField(height=300, vertical_mode="fixed")

# Between 5 and 15 lines, then scrolls
FletCodeField(vertical_mode="bounded", min_lines=5, max_lines=15)

# Grows without limit inside a scrollable column
ft.Column([FletCodeField(vertical_mode="unlimited", wrap=True)], scroll=ft.ScrollMode.AUTO)

FIXED mode needs a bounded height. Without height or a bounded parent, Flutter reports an unbounded-constraints error.


Tokenizer and Language

FletCodeEditor does not use regular expressions. It scans each line with a hand-written, stateful lexer, and you describe the language with Language:

Language(
    words=[WordGroup(["let", "if"], "#C586C0")],   # exact words sharing a color
    line_comments=["#", "//"],
    block_comments=[BlockComment("/*", "*/")],      # may span lines
    strings=[StringRule('"'), StringRule('"""', multiline=True)],
    sigils=[Sigil("$", "#9CDCFE"), Sigil("@", "#4EC9B0")],   # $name, @user
    numbers=True,
    operators="+-*/%=<>!&|^~?:",
    punctuation="(){}[],;.",
    identifier_extra="",          # e.g. "-" to allow my-command
    case_insensitive=False,
    colors=TokenColors(comment=..., string=..., escape=..., number=...,
                       function=..., identifier=..., operator=..., punctuation=...),
)
Field Meaning
words Groups of exact words (keywords, constants, commands), each group with one color.
line_comments Prefixes that comment out the rest of the line.
block_comments Start/end pairs; they can span several lines.
strings StringRule(open, close=None, escape="\\", multiline=False). The escape character and the character after it are colored with colors.escape. An unterminated single-line string ends at the end of the line.
sigils A prefix followed by a name, colored as one token ($var, @user).
numbers Integers, decimals, exponents, _ separators and 0x / 0b / 0o literals.
operators / punctuation Character sets. Runs of operator characters form one token (==, &&).
identifier_extra Extra characters allowed inside names. Letters from any script (including Arabic) are accepted.
colors Colors for the built-in kinds. function colors a name directly followed by (. None keeps the default text color.

Order of matching at each position: whitespace, line comments, block comments, strings, sigils, numbers, words/identifiers, operators, punctuation. Within a kind, the longest opener wins (""" before "). A word listed in words always wins over the function or identifier colors.

Pass the result with language=LANG.to_json(). Changing language at runtime repaints the editor; the editor view may reset its scroll position when you do.


Visible size

width_level and height_level set the visible box of the editor (not its inner content, which scrolls). Each is a number from 1 to 30:

size = level / 30 x available space        (minimum 48 px)
Level Result
30 Covers all the available space
13 (default) About 43% of the available space
1 A small box (about 3%, at least 48 px)

The two levels are independent, so you can enlarge only the width or only the height. The available space is the parent's size (or the screen, when the parent is unbounded).

FletCodeEditor(width_level=20, height_level=8)

The levels are ignored when you set width, height or expand yourself, and height_level is ignored in bounded/unlimited modes (the height then follows the number of lines).

Error underlines

Errors are optional and user-defined. Without errors, nothing is checked. The highlighter colors text as usual, and the checks only add a red underline under the faulty part:

from flet_code_editor_dsl import ErrorRules, BracketPair

Language(
    ...,
    errors=ErrorRules(
        unterminated_strings=True,                       # "text without a closing quote
        brackets=[BracketPair("(", ")"), BracketPair("[", "]")],   # unmatched in a line
        unknown_words=True,                              # names not in `words` / `allowed_words`
        allowed_words=["print", "len"],
        forbidden_words=["goto"],                        # always flagged
        color="#F44747",
        wavy=True,                                       # False = straight underline
    ),
)

All checks work inside one line, so brackets must balance within the same line (do not enable brackets for { } blocks that span lines). Hover messages are not shown yet; only the underline. Turn the underlines off at runtime with show_errors=False.

Code folding

Foldable regions are defined by you in Language, which suits small or unusual DSLs:

from flet_code_editor_dsl import FoldRule

Language(
    ...,
    folds=[
        FoldRule("class", "end"),       # from a line starting with `class` to a line starting with `end`
        FoldRule("on", length=5),       # the 5 lines after a line starting with `on`
        FoldRule("{", "}"),
    ],
    fold_by_indent=False,               # True: also fold by indentation
)
  • A marker matches at the beginning of a line (after indentation). Word markers match whole words only (end does not match endpoint).
  • Start/end pairs can be nested.
  • length folds a fixed number of lines after the start line.
  • Without folds and fold_by_indent, no fold markers are shown. Turn folding off at runtime with code_folding=False.
  • Whether the closing line stays visible when a region is folded is decided by re_editor.

Performance

FletCodeEditor does not use a TextField. Each line is laid out and painted on its own and only the visible lines are built, so editing one line does not re-render the whole document. This is the recommended control for large scripts.

The rest of this section is about FletCodeField, which is a TextField that Flutter paints as a single block. By default (incremental_highlighting=True) the text is highlighted line by line and the result of every line is cached by its content. When you type, only the edited line is re-highlighted; all unchanged lines reuse their cached colors. Rules are also compiled only when rules or syntax_highlighting change, not on every keystroke.

Consequences:

  • Each rule is matched within a single line. A pattern that spans lines (for example a /* ... */ block comment) will not match. Set incremental_highlighting=False to match against the whole text, at the cost of re-highlighting everything on each change.
  • When matches from several rules overlap, the earlier rule in the list wins for each character.
  • Flutter still lays out and paints the editor as a single text block; the saving comes from not re-running the regexes and not rebuilding spans for unchanged lines.

Controlling the Editor at Runtime

Reading the text

def on_change(e):
    print("Current text:", e.data)

editor = FletCodeField(on_change=on_change)

Setting the text or changing options

editor.value = "new content"
editor.line_numbers = False
editor.syntax_highlighting = False
editor.vertical_mode = "bounded"
editor.max_lines = 20
editor.update()

Updating rules

editor.rules = [HighlightRule(pattern=r"\bTODO\b", color="#FFCC00")]
editor.update()

Complete Example

import flet as ft
from flet_code_editor_dsl import FletCodeField, HighlightRule

SAMPLE = '''\
# Example program
let total = (price * 3) + 12
if (total > 100) {
    print("expensive")
} else {
    print("cheap")
}
'''

RULES = [
    HighlightRule(r"\b(let|if|else)\b", "#C586C0"),
    HighlightRule(r"\b\d+(?:\.\d+)?\b", "#B5CEA8"),
    HighlightRule(r'"[^"\n]*"', "#CE9178"),
    HighlightRule(r"#.*", "#6A9955"),
]


def main(page: ft.Page):
    page.title = "flet-code-editor-dsl demo"
    page.theme_mode = ft.ThemeMode.DARK

    def toggle(prop):
        def handler(e):
            setattr(editor, prop, e.control.value)
            editor.update()
        return handler

    def on_mode(e):
        editor.vertical_mode = e.control.value
        editor.update()

    editor = FletCodeField(value=SAMPLE, rules=RULES, height=300)

    page.add(
        ft.Row([
            ft.Switch(label="Line numbers", value=True, on_change=toggle("line_numbers")),
            ft.Switch(label="Colors", value=True, on_change=toggle("syntax_highlighting")),
            ft.Switch(label="Wrap", value=False, on_change=toggle("wrap")),
        ]),
        editor,
    )


ft.run(main)

Important: First Build Required

This package is not pure Python. Its editor widget is written in Dart/Flutter and must be compiled into the Flet client before the control can be displayed.

  1. Declare the library in your app's pyproject.toml under [project] dependencies.
  2. Build your app once with flet build <platform>.
  3. Run normally afterwards. After the first build the control also works with flet run, as long as pyproject.toml still declares the library.
Change Rebuild needed?
Python code, value, rules, line_numbers, syntax_highlighting, wrap, vertical_mode, layout No
Updating flet-code-editor-dsl to a version whose Dart code changed Yes
Upgrading Flet to a new major/minor version Yes
Adding or removing other Flet extensions Yes

Building Your App

# Desktop
flet build windows
flet build macos
flet build linux

# Mobile
flet build apk
flet build ipa

# Web
flet build web

Project Structure

flet-code-editor-dsl/
├── pyproject.toml
├── README.md
└── src/
    ├── flet_code_editor_dsl/
    │   ├── __init__.py
    │   ├── flet_code_editor_dsl.py        # FletCodeEditor, FletCodeField, HighlightRule, VerticalMode
    │   └── language.py                    # Language, WordGroup, StringRule, ...
    └── flutter/
        └── flet_code_editor_dsl/
            ├── pubspec.yaml
            └── lib/
                ├── flet_code_editor_dsl.dart
                └── src/
                    ├── extension.dart             # registers both controls
                    ├── code_editor.dart           # FletCodeEditor (re_editor)
                    ├── tokenizer.dart             # lexer + per-line cache
                    └── flet_code_editor_dsl.dart  # FletCodeField (TextField)

Generated files such as *.egg-info/, __pycache__/, pubspec.lock, .dart_tool/, package_config.json and package_graph.json should not be committed; add them to .gitignore.


Known Limitations

  • Single-line rules by default. See Performance.
  • Regex highlighting only. There is no language parsing, so delimiters inside strings or comments are not treated specially.
  • Dart regex semantics. Patterns must be valid for Dart's RegExp; avoid Python-only syntax such as (?P<name>...).
  • FletCodeField font. It uses a fixed style (Consolas, size 12). Use FletCodeEditor to change font and size.
  • FletCodeEditor and multi-line constructs. Opening a block comment or multi-line string on one line changes the state of the lines below. Those lines are recolored when the editor repaints them; if re_editor keeps already-painted lines as they are, they may keep their old colors until they are redrawn (scrolling, editing them). Test this in your app.
  • Errors and folding. Error checks work per line; fold markers ignore strings and comments (a { inside a string still counts).
  • FletCodeEditor input. It implements its own text input handling; test Arabic (RTL) input and mobile keyboards in your app.
  • First build required. The control is not available in the stock Flet client.

Troubleshooting

Symptom Likely cause and fix
The editor does not appear, or an "unknown control" error is shown The library is not declared in pyproject.toml, or the app was not rebuilt. Add it to dependencies and run flet build.
Unbounded height/constraints error vertical_mode="fixed" without a bounded height. Set height, use expand=True inside a Column, or switch to bounded/unlimited.
A rule has no effect The regex may be invalid for Dart, or syntax_highlighting is False.
Changes made from Python are not visible Call editor.update() after modifying a property.
Compile error in code_editor.dart or tokenizer.dart re_editor APIs change between versions (CodeLineSpanBuilder, CodeLineEditingController); check the version in pubspec.yaml and the error message.
No error underlines or fold markers Language.errors / Language.folds is not set, or show_errors / code_folding is False.
FletCodeEditor shows no colors language is empty or not valid JSON, or syntax_highlighting=False. Pass Language(...).to_json().
Compile error in CodeField after upgrading Parameter names depend on the code_text_field version; check pubspec.yaml.

Contributing

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/my-feature.
  3. Commit your changes with clear messages.
  4. Push the branch and open a Pull Request.

Please open an issue first for larger changes.

License

Licensed under the Apache License, Version 2.0. See the LICENSE file for the full text.

Copyright 2026 obgwew

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Metadata

Release files for flet-code-editor-dsl 0.2.0

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-code-editor-dsl 0.2.0
File Size Uploaded
flet_code_editor_dsl-0.2.0.tar.gz 33.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for flet-code-editor-dsl 0.2.0
File Interpreter ABI Platform
flet_code_editor_dsl-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 61.2 kB

Release files / flet_code_editor_dsl-0.2.0.tar.gz

Download URL flet_code_editor_dsl-0.2.0.tar.gz
Size 33.7 kB
Tags Source
SHA-256 checksum
How to use checksums
06f57cef9066fb6f85c0bb3c9e1ad045d42b6dfb776a2d53af709cc8c1e2549c
BLAKE2b-256 checksum
How to use checksums
a71a7bf6307c4f8f9302caad8bb4ba68cade3bb660801e310b90231852593114
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / flet_code_editor_dsl-0.2.0-py3-none-any.whl

Download URL flet_code_editor_dsl-0.2.0-py3-none-any.whl
Size 27.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a7a02e21cb6b3343a37f6da514482289b595545a4d1b309fd191180d9b09070e
BLAKE2b-256 checksum
How to use checksums
26fc9d449f9f1a96509ae2412fd19d9cf8bf12d4621cbc4c381400da17afcc71
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release history Release notifications | RSS feed

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

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