Metadata-Version: 2.4
Name: fletft
Version: 0.0.7
Summary: Flet add .ft and screen menager
Author-email: Przemek <email@example.com>
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: flet
Requires-Dist: PyYAML

# fletft – Extensions for Flet

**fletft** is an extension for the [Flet](https://flet.dev) library that lets you define UI in separate **`.ft` files (YAML)** and switch views with a simple **ScreenManager**.

- Layout in YAML, logic in Python
- Load controls with one call
- Optional page properties (`title`, `theme_mode`, …) from the same file
- Custom / third-party controls via Python registration
- Clear validation errors instead of long tracebacks

**Requirements:** Python ≥ 3.8, Flet ≥ 0.80 (tested with **Flet 0.86.x**).

**Import package name:** `flet_ft` (not `fletft`).

```python
from flet_ft import load_ui_into, get_by_id, ScreenManager
```

---

## Features

| Feature | Description |
|--------|-------------|
| **`.ft` (YAML) UI** | Declare controls as nested YAML; separate presentation from logic |
| **`load_ui` / `load_ui_from_file` / `load_ui_into`** | Parse YAML → Flet controls; optionally apply page props and attach to `page` |
| **`apply_page`** | Set `page.title`, `padding`, `theme_mode`, `bgcolor`, … from YAML |
| **`id` + `get_by_id`** | Register controls by id and access them from Python |
| **Event handlers** | `on_click: my_fn` resolved via `context_globals=globals()` |
| **Custom components** | `register_module` / `register_component` (Python only) |
| **ScreenManager** | Multi-screen navigation with routes |
| **Validation** | `UnknownComponentError`, `InvalidYAMLError` with readable messages |

---

## Installation

```bash
pip install 'flet[all]' fletft
```

**TestPyPI** (pre-release builds):

```bash
pip install -U \
  --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  fletft
```

---

## How `.ft` files work (rules)

A `.ft` file is **YAML**. fletft maps it to Flet control constructors.

### 1. Recommended root structure

```yaml
page:
  # optional page properties (see apply_page)
  title: "My app"
  padding: 20
  theme_mode: DARK
  bgcolor: BLUE_GREY_900
  horizontal_alignment: CENTER
  vertical_alignment: CENTER

  controls:          # list of root controls
    - Text:
        value: "Hello"
        size: 24
```

**Rules:**

- Top key is usually `page`.
- UI tree lives under `page.controls` (a **list**).
- Keys other than `controls` under `page` are treated as **page properties** when you use `apply_page` / `load_ui_into`.

### 2. Alternative roots

**List of controls only:**

```yaml
- Text:
    value: "A"
- Text:
    value: "B"
```

**Single control:**

```yaml
Column:
  controls:
    - Text:
        value: "Solo"
```

### 3. Control node shape

Every control is a **mapping with one key** = Flet class name:

```yaml
- ClassName:
    prop1: value1
    prop2: value2
```

| YAML | Meaning |
|------|---------|
| `Text`, `Column`, `Row`, `Container`, … | Built-in Flet classes (`flet.Text`, …) |
| `fdt.DataTable2` | Prefixed custom control (after `register_module("fdt", …)`) |
| Nested under `controls:` | List of child controls |
| Nested under `content:` | Single child control |
| Other lists (`tabs:`, `actions:`, …) | Also parsed as lists of controls when items are control nodes |
| `id: "my_id"` | Stored in fletft registry only (not passed to the constructor) |
| `on_click: handler_name` | String → function from `context_globals` |
| Other keys | Keyword arguments to the Flet constructor |

### 4. Nesting

Children of layout controls go in `controls`:

```yaml
- Column:
    spacing: 12
    controls:
      - Text:
          value: "Title"
      - Row:
          controls:
            - ElevatedButton:
                content: "OK"
            - ElevatedButton:
                content: "Cancel"
```

Single-child controls use `content`:

```yaml
- Container:
    padding: 10
    content:
      Text:
        value: "Inside"
```

### 5. Events (handlers)

In YAML the value is a **function name** (string). In Python pass the namespace:

```python
load_ui_from_file("ui.ft", context_globals=globals())
```

```yaml
- ElevatedButton:
    content: "Save"
    on_click: on_save
```

```python
def on_save(e):
    print("saved")
```

If the handler is missing → `InvalidYAMLError` with a clear message.

### 6. `id` and `get_by_id`

```yaml
- Text:
    value: "Hi"
    id: title
```

```python
from flet_ft import get_by_id

get_by_id("title").value = "Updated"
page.update()
```

### 7. Enums, icons, colors (strings)

You may write plain names or qualified names; fletft tries to resolve them:

```yaml
theme_mode: DARK                 # or ThemeMode.DARK
icon: HOME                       # or Icons.HOME
bgcolor: BLUE_GREY_900           # or Colors.BLUE_GREY_900
weight: BOLD
horizontal_alignment: CENTER
```

Resolution order: `flet.<Namespace>.<NAME>`, then common namespaces (`Icons`, `Colors`, `ThemeMode`, …).

### 8. Flet 0.86+ API notes (important)

Use current Flet constructor names in YAML:

| Avoid (old docs) | Use (Flet 0.86) |
|------------------|-----------------|
| `ElevatedButton: { text: "OK" }` | `ElevatedButton: { content: "OK" }` |
| Old `Tabs` with `tabs: [Tab(text=..., content=...)]` | `Tabs` + `length` + `TabBar` + `TabBarView` (see example below) |
| `flet.app(target=main)` | `ft.run(main)` preferred (both often work) |

**Tabs pattern (Flet 0.86):**

```yaml
page:
  controls:
    - Tabs:
        length: 2
        expand: true
        selected_index: 0
        content:
          Column:
            expand: true
            controls:
              - TabBar:
                  tabs:
                    - Tab:
                        label: "Home"
                    - Tab:
                        label: "Settings"
              - TabBarView:
                  expand: true
                  controls:
                    - Text:
                        value: "Home content"
                    - Text:
                        value: "Settings content"
```

`length` must match the number of tabs / views.

### 9. What belongs in YAML vs Python

| In `.ft` | In Python |
|----------|-----------|
| Structure, labels, layout, styles | Business logic, handlers |
| Static trees | Dynamic lists built in code (or extend later) |
| `id` for later access | `get_by_id`, state, async, I/O |
| — | `register_module` / `register_component` |

### 10. Validation & common mistakes

| Problem | Result |
|---------|--------|
| Unknown class name | `UnknownComponentError` |
| Bad YAML syntax | `InvalidYAMLError` |
| Missing handler | `InvalidYAMLError` |
| Wrong constructor kwarg (e.g. `text=` on button) | `InvalidYAMLError` wrapping Flet `TypeError` |
| Empty component props (`- Divider:`) | Treated as `{}` (allowed) |

Always match **Flet’s current** parameter names from the [Flet docs](https://docs.flet.dev).

---

## Quick start

**`ui.ft`**

```yaml
page:
  title: "fletft quick start"
  padding: 20
  theme_mode: DARK
  controls:
    - Column:
        spacing: 16
        controls:
          - Text:
              value: "Hello fletft"
              size: 28
              id: title
          - ElevatedButton:
              content: "Click me"
              id: btn
              on_click: on_click
```

**`main.py`**

```python
import flet as ft
from flet_ft import load_ui_into, get_by_id


def on_click(e):
    get_by_id("title").value = "Clicked!"
    e.page.update()


def main(page: ft.Page):
    load_ui_into(page, "ui.ft", context_globals=globals())


if __name__ == "__main__":
    ft.run(main)
```

`load_ui_into` = `apply_page` + load controls + `page.controls.extend` + `page.update()`.

---

## Custom & third-party controls (Python only)

YAML **cannot** import packages. Register them in Python, then use names in `.ft`.

```python
import flet as ft
import flet_datatable2 as fdt
from flet_ft import register_module, register_component, load_ui_into

register_module("fdt", fdt)          # → fdt.DataTable2 in YAML
# register_component("MyButton", MyButton)

def main(page: ft.Page):
    load_ui_into(page, "ui.ft", context_globals=globals())

    # Or per-call (no global registry):
    # load_ui_into(page, "ui.ft", modules={"fdt": fdt}, components={"MyButton": MyButton}, ...)

ft.run(main)
```

```yaml
page:
  controls:
    - fdt.DataTable2:
        expand: true
    # - MyButton:
    #     content: "OK"
```

| API | Role |
|-----|------|
| `register_module(prefix, module)` | All public types as `prefix.TypeName` |
| `register_component(name, cls)` | One alias, e.g. `MyButton` |
| `load_ui(..., modules={...}, components={...})` | Same, only for this load |
| `list_registered_modules()` / `list_registered_components()` | Inspect |
| `clear_registries()` | Clear module/component registries |

---

## ScreenManager

```python
import flet as ft
from flet_ft import ScreenManager


def main(page: ft.Page):
    page.title = "Screen Manager"
    sm = ScreenManager(page)

    home = ft.Column(
        [
            ft.Text("Home", size=30),
            ft.ElevatedButton(
                content="Go to About",
                on_click=lambda e: sm.set_current("about"),
            ),
        ],
        horizontal_alignment=ft.CrossAxisAlignment.CENTER,
    )

    about = ft.Column(
        [
            ft.Text("About", size=30),
            ft.ElevatedButton(
                content="Back",
                on_click=lambda e: sm.set_current("home"),
            ),
        ],
        horizontal_alignment=ft.CrossAxisAlignment.CENTER,
    )

    sm.add_screen("home", home)                    # route default: /home
    sm.add_screen("about", about, route="/about")
    sm.set_current("home")


ft.run(main)
```

- `add_screen(name, view, route=None)` — `view` is a control, list of controls, or `ft.View`
- `set_current(name)` — switches `page.views` and navigates to the route  
Compatible with Flet 0.86 (`View(controls=..., route=...)`).

---

## API reference

### Loading UI

```text
load_ui(yaml_data, context_globals=None, clear_ids_flag=True, modules=None, components=None) -> list
load_ui_from_file(path, context_globals=None, clear_ids_flag=True, modules=None, components=None) -> list
load_ui_into(page, file_or_yaml, context_globals=None, clear_ids_flag=True, modules=None, components=None) -> list
apply_page(page, file_or_yaml) -> data | None
```

**Page properties** applied by `apply_page` / `load_ui_into` (when present under `page:`):  
`title`, `padding`, `bgcolor`, `theme_mode`, `horizontal_alignment`, `vertical_alignment`, `scroll`, `auto_scroll`, `spacing`, `rtl`, `fonts`, `theme`, `dark_theme`, `window`.

### IDs

```text
get_by_id(id) -> Control | None
clear_ids()
list_ids() -> list[str]
```

### Registration

```text
register_module(prefix, module)
register_component(name, cls)
unregister_module(prefix)
unregister_component(name)
clear_registries()
list_registered_modules() -> list[str]
list_registered_components() -> list[str]
```

### Exceptions

```text
FletFTError
UnknownComponentError
InvalidYAMLError
```

### ScreenManager

```text
ScreenManager(page)
  .add_screen(name, view, route=None)
  .set_current(name)
```

---

## Manual load (without `load_ui_into`)

```python
import flet as ft
from flet_ft import apply_page, load_ui_from_file, get_by_id

def main(page: ft.Page):
    apply_page(page, "ui.ft")
    controls = load_ui_from_file("ui.ft", context_globals=globals())
    page.controls.extend(controls)
    page.update()

ft.run(main)
```

---

## JSON Schema (optional)

Editor support / validation:

- Release asset: [fletft-schema.json](https://github.com/pp1sp1/fletft/releases/download/schema/fletft-schema.json)

```bash
curl -O https://raw.githubusercontent.com/pp1sp1/fletft/refs/heads/main/fletft-schema.json
```

Point your editor’s YAML schema setting at this file for `.ft` files if supported.

---

## Links

- Flet: [https://flet.dev](https://flet.dev)
- Flet controls docs: [https://docs.flet.dev](https://docs.flet.dev)
- PyPI: [https://pypi.org/project/fletft/](https://pypi.org/project/fletft/)

---

## License

See the package license on PyPI / repository.
```
