Skip to content

Commit aada174

Browse files
raivolinkclaudemikahanninen
authored
Update assistant package (#1321)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Mika Hänninen <mika.hanninen@gmail.com>
1 parent 042c325 commit aada174

11 files changed

Lines changed: 967 additions & 517 deletions

File tree

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Migration from robocorp-flet to Official Flet
2+
3+
## Overview
4+
5+
The `rpaframework-assistant` package was migrated from `robocorp-flet==0.4.2.3` (an outdated fork of flet 0.4.2) to the official `flet>=0.82.0,<1.0.0` package. This was necessary because the fork was no longer maintained and fell far behind upstream flet, which has undergone major breaking changes — most notably the [v0.80 "1.0 Alpha" overhaul](https://flet.dev/blog/flet-v0-80-release-announcement) that merged `flet_core` into `flet` and redesigned many APIs.
6+
7+
## Reason for Migration
8+
9+
- **`robocorp-flet` is unmaintained** — pinned to a fork of flet 0.4.2 with no further updates.
10+
- **Security and compatibility** — staying on an outdated fork blocks Python version upgrades and misses upstream fixes.
11+
- **`flet_core` was removed** — flet 0.80+ merged `flet_core` into `flet`, making the old import paths (`from flet_core import ...`) invalid.
12+
13+
## Python Version Requirement
14+
15+
`requires-python` was bumped from `>=3.9.1` to `>=3.10` because flet 0.82+ requires Python 3.10 at minimum.
16+
17+
## Major Changes by File
18+
19+
### `background_flet.py` — Full Rewrite
20+
21+
**Why:** The old code relied on private flet internals that were completely removed:
22+
- `ft.__connect_internal_sync` — internal connection setup
23+
- `ft.open_flet_view` / `ft.close_flet_view` — subprocess-based window management
24+
- `flet_core.page.Connection` — internal connection class
25+
26+
**New approach:**
27+
- Runs `ft.app(target=...)` in a daemon `threading.Thread`
28+
- Patches `signal.signal` to no-op in the thread (signal handlers can't be registered outside the main thread)
29+
- Uses `threading.Event` for close detection (replaces `subprocess.Popen.poll()`)
30+
- `close_flet_view()` calls `page.window.destroy()` via `asyncio.run_coroutine_threadsafe` instead of subprocess termination
31+
- `start_flet_view()` has a 30-second timeout on page readiness to prevent indefinite hangs if flet fails to start
32+
- `poll()` returns `None` (still open) or `0` (closed), preserving the existing interface
33+
34+
**Reference:** [Flet Page Window events](https://flet.dev/docs/controls/page#window)
35+
36+
### `flet_client.py` — Window API and Service Changes
37+
38+
**Window properties renamed** (flet 0.80+ change):
39+
40+
| Old (0.4.x) | New (0.82+) |
41+
|---|---|
42+
| `page.window_height` | `page.window.height` |
43+
| `page.window_width` | `page.window.width` |
44+
| `page.window_always_on_top` | `page.window.always_on_top` |
45+
| `page.window_center()` | `page.window.center()` |
46+
| `page.window_left` | `page.window.left` |
47+
| `page.window_top` | `page.window.top` |
48+
| `page.window_destroy()` | `page.window.destroy()` |
49+
50+
**Reference:** [Flet Window properties migration](https://flet.dev/docs/controls/page#window)
51+
52+
**FilePicker moved from overlay to services:**
53+
`FilePicker` is now a `Service` subclass. Invisible elements that are services are added to `page.services` instead of `page.overlay`.
54+
55+
**Reference:** [Flet Services](https://flet.dev/docs/controls/filepicker)
56+
57+
**Dropdown `on_change` renamed to `on_select`:**
58+
The `Dropdown` control no longer has `on_change`. The equivalent event handler is `on_select`. Other controls (`TextField`, `Checkbox`, `Slider`, `RadioGroup`) still use `on_change`.
59+
60+
**Thread-safe `page.update()` via `flet_update()`:**
61+
In flet 0.82+, calling `page.update()` from a thread other than the flet event loop thread corrupts flet's internal state and prevents `window.destroy()` from closing the app. The `flet_update()` method now detects whether it's being called from the flet thread or not, and routes cross-thread calls through `loop.call_soon_threadsafe(page.update)`. This is critical for button callbacks, which are queued as `pending_operation` and executed on the main thread.
62+
63+
**AppBar.actions defaults to None:**
64+
In the old flet, `AppBar.actions` was initialized as an empty list. In 0.82+, it defaults to `None` and must be initialized before appending.
65+
66+
### `library.py` — API Renames
67+
68+
**Renamed modules:**
69+
70+
| Old | New |
71+
|---|---|
72+
| `from flet_core import ...` | `from flet import ...` |
73+
| `from flet_core.control_event import ControlEvent` | `from flet import ControlEvent` |
74+
| `from flet_core.dropdown import Option` | `from flet import DropdownOption` |
75+
| `colors` (module) | `Colors` (enum) |
76+
| `icons` (module) | `Icons` (enum) |
77+
78+
**Icon constructor:**
79+
`flet.Icon(name=...)` changed to `flet.Icon(icon=...)`.
80+
81+
**Icon names must use `Icons` enum:**
82+
In flet 0.82+, passing icon names as lowercase strings (e.g. `"check_circle_rounded"`) no longer renders correctly — the icon appears as a blank white area. String names must be converted to the `Icons` enum via `getattr(Icons, icon.upper(), icon)`. The `add_icon` method already used `Icons` enum constants; `add_flet_icon` was updated to convert user-provided strings.
83+
84+
**FilePicker API change:**
85+
The old callback-based `FilePicker(on_result=callback)` pattern was replaced. In flet 0.82+, `pick_files()` is async and returns a list of `FilePickerFile` directly. The `FilePickerResultEvent` class was removed. The click handler must be `async def` and `await` the `pick_files()` call.
86+
87+
**FilePicker service registration:**
88+
`page.services.append()` is a plain list that doesn't mount controls. Must use `page._services.register_service()` to properly register services like FilePicker so they get a page reference.
89+
90+
**`page.launch_url()` is now async:**
91+
In flet 0.82+, `page.launch_url()` is a coroutine. Event handlers that call it must wrap it with `asyncio.ensure_future()` to schedule it on the event loop.
92+
93+
**Reference:** [Flet FilePicker](https://flet.dev/docs/controls/filepicker)
94+
95+
### `types.py` — Alignment Constants Removed
96+
97+
The convenience constants `alignment.center`, `alignment.top_left`, etc. were removed. Replaced with explicit `Alignment(x, y)` values:
98+
99+
| Old constant | New value |
100+
|---|---|
101+
| `alignment.top_left` | `Alignment(-1, -1)` |
102+
| `alignment.top_center` | `Alignment(0, -1)` |
103+
| `alignment.top_right` | `Alignment(1, -1)` |
104+
| `alignment.center_left` | `Alignment(-1, 0)` |
105+
| `alignment.center` | `Alignment(0, 0)` |
106+
| `alignment.center_right` | `Alignment(1, 0)` |
107+
| `alignment.bottom_left` | `Alignment(-1, 1)` |
108+
| `alignment.bottom_center` | `Alignment(0, 1)` |
109+
| `alignment.bottom_right` | `Alignment(1, 1)` |
110+
111+
**Reference:** [Flet Alignment](https://flet.dev/docs/controls/container#alignment)
112+
113+
### `library.py` — State Cleanup Between Dialogs
114+
115+
After each `run_dialog` / `ask_user` call, the following state is now cleared to prevent leaks between sequential dialogs:
116+
- `results` — user input values
117+
- `date_inputs` — names of date fields (for string→date conversion)
118+
- `_required_fields` — names of required fields
119+
- `_open_layouting` — layout stack (Row/Column/Stack/Container/AppBar)
120+
- `validation_errors` — validation error messages
121+
122+
**Date input empty value handling:**
123+
`_get_results()` now returns `None` for empty date fields instead of crashing with `ValueError: Invalid isoformat string: ''`.
124+
125+
### `callback_runner.py` — Import Path Only
126+
127+
Changed `from flet_core import ControlEvent` to `from flet import ControlEvent`. No logic changes.
128+
129+
## Files Modified
130+
131+
| File | Change scope |
132+
|---|---|
133+
| `packages/assistant/pyproject.toml` | Dependency + Python version |
134+
| `packages/assistant/src/RPA/Assistant/background_flet.py` | Full rewrite, startup timeout |
135+
| `packages/assistant/src/RPA/Assistant/callback_runner.py` | Import path |
136+
| `packages/assistant/src/RPA/Assistant/flet_client.py` | Window API, services, Dropdown event, thread-safe update |
137+
| `packages/assistant/src/RPA/Assistant/library.py` | Renames, FilePicker (async), Icon enum, launch_url async, state cleanup, date input empty handling |
138+
| `packages/assistant/src/RPA/Assistant/types.py` | Import path, alignment constants |
139+
140+
## Future Considerations
141+
142+
### Improve window close error handling
143+
144+
`close_flet_view()` in `background_flet.py` uses a broad `except Exception: pass` that silently swallows all errors from `window.destroy()`. This can mask real failures and make the dialog appear closed when the native window is still open. A future improvement would be to log the error instead of silently ignoring it, and ensure `_closed_event` is only set after the close operation actually succeeds or a real close/disconnect event is observed. Note that `close_flet_view` also serves as an `atexit` handler, so error propagation must be handled carefully.
145+
146+
### Replace private flet service internals with public API
147+
148+
`flet_client.py` uses `page._services.register_service()` and `page._services.unregister_services()` — private flet internals that may change without notice. This was necessary because the public `page.services.append()` does not properly mount controls (the `FilePicker` fails with "Control must be added to the page first"). If a future flet version provides a public service registration API, these calls should be migrated. Until then, this is documented tech debt.
149+
150+
### Expose additional flet layout properties
151+
152+
The Assistant library keywords expose only a subset of the underlying flet control properties. The following are available in flet 0.82+ and would be useful additions:
153+
154+
**Quick wins — high impact, easy to add:**
155+
156+
| Keyword | Property | Description |
157+
|---|---|---|
158+
| `open_row` | [`alignment`](https://flet.dev/docs/controls/row#alignment) | Position children along main axis (START, END, CENTER, SPACE_BETWEEN) |
159+
| `open_row` | [`vertical_alignment`](https://flet.dev/docs/controls/row#vertical_alignment) | Align items vertically within the row |
160+
| `open_row` | [`spacing`](https://flet.dev/docs/controls/row#spacing) | Gap between children (default 10) |
161+
| `open_column` | [`horizontal_alignment`](https://flet.dev/docs/controls/column#horizontal_alignment) | Left/center/right align content |
162+
| `open_column` | [`alignment`](https://flet.dev/docs/controls/column#alignment) | Vertical distribution of children |
163+
| `open_column` | [`spacing`](https://flet.dev/docs/controls/column#spacing) | Gap between children |
164+
| `open_container` | [`alignment`](https://flet.dev/docs/controls/container#alignment) | Position content within container |
165+
| `open_navbar` | [`bgcolor`](https://flet.dev/docs/controls/appbar#bgcolor) | Navbar background color |
166+
167+
**Medium priority:**
168+
169+
| Keyword | Property | Description |
170+
|---|---|---|
171+
| `open_row` / `open_column` | [`wrap`](https://flet.dev/docs/controls/row#wrap) | Wrap children to next line when out of space |
172+
| `open_container` | [`border_radius`](https://flet.dev/docs/controls/container#border_radius) | Rounded corners |
173+
| `open_container` | [`border`](https://flet.dev/docs/controls/container#border) | Border styling (color, width) |
174+
| `open_container` | [`shadow`](https://flet.dev/docs/controls/container#shadow) | Drop shadow effect |
175+
| `open_navbar` | [`center_title`](https://flet.dev/docs/controls/appbar#center_title) | Center the title text |
176+
| `open_navbar` | [`leading`](https://flet.dev/docs/controls/appbar#leading) | Back button or menu icon |
177+
| `open_stack` | [`alignment`](https://flet.dev/docs/controls/stack#alignment) | Default position for non-positioned children |
178+
| `add_submit_buttons` | [`icon`](https://flet.dev/docs/controls/elevatedbutton#icon) | Icons on buttons |
179+
180+
**Advanced:**
181+
182+
| Keyword | Property | Description |
183+
|---|---|---|
184+
| `open_container` | [`gradient`](https://flet.dev/docs/controls/container#gradient), [`image`](https://flet.dev/docs/controls/container#image) | Background gradient or image |
185+
| `open_stack` | [`fit`](https://flet.dev/docs/controls/stack#fit), [`clip_behavior`](https://flet.dev/docs/controls/stack#clip_behavior) | Child sizing strategy, overflow clipping |
186+
| `add_submit_buttons` | [`style`](https://flet.dev/docs/controls/elevatedbutton#style) | Per-button colors, elevation, shape |
187+
188+
## External References
189+
190+
- [Flet 0.80 Release Announcement](https://flet.dev/blog/flet-v0-80-release-announcement) — breaking changes overview
191+
- [Flet Documentation](https://flet.dev/docs/) — official docs
192+
- [Flet Page / Window API](https://flet.dev/docs/controls/page#window) — window property changes
193+
- [Flet FilePicker](https://flet.dev/docs/controls/filepicker) — new FilePicker API
194+
- [Flet PyPI](https://pypi.org/project/flet/) — package versions

packages/assistant/pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description = "Interactive UI library for RPA Framework"
55
authors = [{name = "RPA Framework", email = "rpafw@robocorp.com"}]
66
license = {text = "Apache-2.0"}
77
readme = "README.rst"
8-
requires-python = ">=3.9.1"
8+
requires-python = ">=3.10"
99

1010
keywords = ["robotframework", "rpa", "automation", "dialogs", "assistant"]
1111
classifiers = [
@@ -17,7 +17,6 @@ classifiers = [
1717
"Topic :: Software Development :: Libraries",
1818
"Framework :: Robot Framework :: Library",
1919
"Framework :: Robot Framework",
20-
"Programming Language :: Python :: 3.9",
2120
"Programming Language :: Python :: 3.10",
2221
"Programming Language :: Python :: 3.11",
2322
"Programming Language :: Python :: 3.12",
@@ -26,7 +25,8 @@ classifiers = [
2625
dependencies = [
2726
"robotframework>=4.0.0,!=4.0.1,!=6.1.0,<8.0.0",
2827
"rpaframework-core>=12.1.0",
29-
"robocorp-flet==0.4.2.3",
28+
"flet~=0.82.0",
29+
"flet-desktop~=0.82.0",
3030
"typing-extensions>=4.4.0",
3131
]
3232

0 commit comments

Comments
 (0)