pypi flet 0.86.0
v0.86.0

7 hours ago

New features

  • Add support for Python multiprocessing in packaged Flet desktop apps built with flet build macos, flet build windows, and flet build linux. multiprocessing APIs such as Process, ProcessPoolExecutor, the spawn/forkserver start methods, and the resource tracker now work in packaged desktop apps. Previously, worker processes re-executed sys.executable, which pointed to the app binary itself, causing each worker to launch another GUI app instance and hang. Flet desktop runners now detect CPython multiprocessing child command lines before Flutter starts and divert them to a headless embedded Python interpreter via dart_bridge 1.5.0+. The Python bootstrap also runs the app module as the real sys.modules["__main__"] with python -m semantics, so top-level worker functions in main.py can be pickled correctly. When using multiprocessing, your app must follow normal Python multiprocessing rules: guard ft.run(...) with if __name__ == "__main__":, define worker functions at module top level, and do not access Flet UI objects from worker processes. See the new Multiprocessing cookbook page. Mobile platforms remain unsupported because iOS and Android do not allow apps to spawn arbitrary child processes (#4283, #6577) by @ndonkoHenri.
  • Multi-version bundled CPython support in flet build and flet publish. Pick the runtime your app ships with via the new --python-version flag (3.12 / 3.13 / 3.14), or let it be derived from [project].requires-python in your pyproject.toml; defaults to the latest supported stable (currently 3.14). The matching CPython-standalone build, Pyodide release (0.27.7 / 0.29.4 / 314.0.1), and Emscripten wheel platform tag are all resolved from flet-dev/python-build's date-keyed manifest. Adding a future pre-release CPython line (e.g. 3.15 beta) is a one-row append with prerelease=True — opt-in only via an explicit --python-version 3.15 or requires-python = "==3.15.*", never the auto-resolved default. Requires serious_python >= 4.0.0, now pinned in the flet build template. See the new Choosing a Python version docs section (#6577) by @FeodorFitsner.
  • Add ft.DataChannel: dedicated byte channels for widgets that move bulk binary data (image frames, audio buffers, ML tensors) between Dart and Python, bypassing the MsgPack control protocol. The Dart side opens a channel via FletBackend.of(context).openDataChannel() and announces it to Python by firing a data_channel_open control event with {channel_name, channel_id}; the Python side declares on_data_channel_open: Optional[ft.EventHandler[ft.DataChannelOpenEvent]] and captures the channel via self.get_data_channel(e.channel_id). Backed by a dedicated PythonBridge per channel in embedded native mode (4–7 GiB/s on M2 Pro) and by the default ProtocolMuxedDataChannelFactory in dev / web modes (raw-byte frames muxed over the active Flet protocol transport with a 1-byte type discriminator). Pyodide gets zero-copy outbound sends via postMessage Transferable ArrayBuffer. First consumer: flet-charts MatplotlibChartCanvas, migrated from _invoke_method PNG dispatch to a 1-byte-opcode data channel by @FeodorFitsner.
  • In-process Python transport (dart_bridge FFI). package:flet gains a third protocol transport alongside the UDS / TCP socket servers: it can run Flet's MsgPack protocol over an in-process dart_bridge byte channel via a FletApp(channelBuilder: …) seam (the flet package stays Python-independent — it doesn't depend on serious_python or know about PythonBridge; the embedder wires the channel). serious_python >= 3.0.0 uses this seam to embed the Python interpreter in-process instead of talking to it over a localhost socket, and the flet build template migrates from sockets to the FFI transport. On Android, where the OS may keep the process alive across a back-button quit and restart only the Dart VM, the transport rebinds to the new VM's dart_bridge ports on a session-restart signal (libdart_bridge >= 1.3.0) — the Python process and its in-memory state are preserved and the Flet session is rebuilt from REGISTER_CLIENT by @FeodorFitsner.
  • Add flet clean command that deletes the build directory of a Flet app — the Flutter bootstrap project, cached artifacts, and generated output — in a single step (#6233) by @ndonkoHenri.
  • Add compression_quality to FilePicker.pick_files() for selecting the image compression quality used by supported platforms (#6573) by @ndonkoHenri.
  • Add ConsentManager to flet-ads for gathering user consent (e.g. GDPR/EEA) via Google's User Messaging Platform (UMP) before requesting ads (#6569, #6615) by @ndonkoHenri.
  • Add ft.RawImage: a high-bandwidth pixel-frame display control for Pillow output, NumPy arrays, camera frames and procedural graphics. Frames stream over a dedicated ft.DataChannel instead of the control protocol: raw premultiplied RGBA straight to a GPU texture on local transports (desktop, flet run, Pyodide), automatic PNG fallback on remote flet-web sessions. The awaitable render(pil_or_numpy) / render_rgba(w, h, bytes) / render_encoded(png_jpeg_webp_bytes) methods resolve when the client has displayed the frame, so a plain while True: await ri.render(...) loop self-paces to display speed. Premultiplied-alpha conversion runs in Pillow's C loops with an opaque fast-path; Pillow and NumPy remain optional. The last frame is retained and replayed on remount, mirroring Image.src. Ships with five gallery examples (photo viewer, plasma, Pillow paint, Mandelbrot explorer, Game of Life) and a docs page with RawImage vs Image guidance (#6674) by @FeodorFitsner.

Improvements

  • Swift Package Manager for iOS/macOS builds (on by default). flet build / flet debug now integrate the embedded Python runtime via SPM instead of CocoaPods (CocoaPods goes read-only in December 2026). Flet auto-falls back to CocoaPods when the app depends on a package that isn't SPM-ready — currently flet-video (media_kit) — since Flutter then builds the whole app with CocoaPods. Force CocoaPods for other non-SPM packages with --no-swift-package-manager (or swift_package_manager = false under [tool.flet]). Flet does not change Flutter's global SPM configuration; the setting only selects how serious_python stages the runtime to match. When SPM is used (it has no pod install hook), flet build sets SERIOUS_PYTHON_DARWIN_SPM so serious_python's package step stages the runtime (Python/dart_bridge xcframeworks, the iOS native extensions, and the stdlib/site-packages/app resources) into the plugin's Package.swift layout on the host before flutter build, and exports the SP_NATIVE_SET cache-bust key into the build. Requires the SPM-capable serious_python release.
  • Smaller Android apps with no native-packaging config. flet build apk/aab consume serious_python's new Android packaging: Python extension modules load memory-mapped directly from the APK (no extraction to disk), and pure Python ships in stored asset zips read via zipimport, so the standard library is no longer duplicated per ABI. Apps no longer need useLegacyPackaging / keepDebugSymbols — the flet build Android template drops them; just use minSdk 23+. New --android-extract-packages flag and [tool.flet.android].extract_packages ship "path-hungry" packages — those that read bundled data via __file__ / pkg_resources instead of importlib.resources — extracted to disk instead of inside the zip (most packages, including certifi, are zip-safe and need no entry). Requires serious_python with the native-mmap packaging (dart_bridge 1.4.0).
  • Pyodide is no longer pre-baked into the flet build template. Each flet build web / flet publish run downloads the matching pyodide-core-<version>.tar.bz2 (plus the runtime micropip and packaging wheels) into a per-version cache at ~/.flet/pyodide/<version>/ and copies the files into the build output. Subsequent builds reuse the cache; the older 0.27.5 bundle previously shipped in the cookiecutter template is gone (#6577) by @FeodorFitsner.
  • The supported Python / Pyodide / dart_bridge versions are loaded on demand from flet-dev/python-build's date-keyed manifest.json (fetched once and cached under ~/.flet/cache), the single source of truth shared with serious_python — replacing flet's hand-mirrored version table. flet build forwards only SERIOUS_PYTHON_VERSION and lets serious_python derive the full version / build date / dart_bridge version from its own committed snapshot. The module exposes get_supported_python_versions() / get_default_python_version() (the previous SUPPORTED_PYTHON_VERSIONS / DEFAULT_PYTHON_VERSION constants are removed) (#6577) by @FeodorFitsner.
  • flet --version shows just the Flet and Flutter versions; the static Pyodide: … line and the global flet.version.pyodide_version export are removed (the supported Python / Pyodide set now lives in python-build's manifest, not the CLI output) (#6577) by @FeodorFitsner.
  • flet --version --json emits a machine-readable document — Flet/Flutter versions and the Linux build dependencies — for CI to read via jq instead of importing Flet internals with python -c. (The supported Python/Pyodide table is no longer included; it comes from python-build's manifest.) The canonical Linux apt dependency list moved from flet.utils.linux_deps (runtime package) to flet_cli.utils.linux_deps (build tooling) by @FeodorFitsner.
  • client/web/python.js and the build template's python.js no longer hardcode defaultPyodideUrl. patch_index.py now injects flet.pyodideUrl per build (CDN URL by default, or the local pyodide/pyodide.js path under --no-cdn) so the runtime URL always tracks the resolved Pyodide release (#6577) by @FeodorFitsner.
  • Stream-oriented Flet protocol transports (UDS / TCP used by flet run dev mode) now use length-prefixed framing instead of streaming msgpack.Unpacker.feed. Combined with a new 1-byte type discriminator at the head of every packet (0x00 = MsgPack control frame, 0x01 = raw DataChannel frame), this unifies framing across all transports (sockets, WebSocket, dart_bridge FFI, Pyodide postMessage). StreamingMsgpackDeserializer is removed from package:flet; each inbound packet is one complete MsgPack value, decoded one-shot via msgpack.deserialize(bytes) by @FeodorFitsner.
  • Bump the bundled Flutter to 3.44.2 (from 3.41.7). The Flet client and the flet build template migrate to Flutter 3.44's built-in Kotlin (the Android app no longer applies the Kotlin Gradle plugin itself) and Java 17; the client's Gradle wrapper moves to 8.14 by @FeodorFitsner.
  • Raw RGBA Matplotlib frames on local transports. MatplotlibChart now skips per-frame diffing and PNG encode/decode when the client runs on the same machine: uncompressed RGBA full frames stream straight from Agg's buffer over the chart's DataChannel (new 0x04 opcode) and are displayed with a single decodeImageFromPixels + swap on the Dart side. A 1600×1000 @ DPR 2 figure (24 MB/frame) over a local socket goes from 7.4 to 18.7 fps, leaving matplotlib's own render as the dominant per-frame cost; remote WebSocket clients keep the compact PNG full+diff pipeline. The format is auto-selected via the new Connection.local_data_transport capability flag (set by the socket, dart_bridge and Pyodide transports). Also fixes O(n²) length-prefixed packet reassembly in the Dart socket transport — multi-MB frames arrive in dozens of chunks and previously re-flattened the accumulation buffer on every chunk (#6673) by @FeodorFitsner.
  • flet build web and flet publish now default the web renderer to canvaskit instead of auto. With auto, Chromium selects the dart2wasm/skwasm renderer, whose JS↔Dart typed-data boundary costs make byte-streaming Pyodide apps (matplotlib frames, RawImage) ~6–7x slower per frame; pass --renderer auto or set [tool.flet.web].renderer to restore the old behavior. Also fixes tool.flet.web.renderer being ignored by flet publish (shadowed by an argparse default) (#6673) by @FeodorFitsner.
  • Faster mobile cold start: import flet is now lazy. The flet package previously executed its full ~270-module public API eagerly on import flet; it now resolves public names on first access via a module-level __getattr__ (PEP 562), so an app loads only the modules it actually uses. On a mid-range Android device this cut import flet from ~2.0s to ~0.15s. The eager subsystem clusters that Page pulled in (auth, components/hooks, Cupertino controls) are deferred too. Type checkers, IDEs, and from flet import * are unaffected (#6597) by @FeodorFitsner.

Breaking changes

  • App files now ship unpacked in a read-only bundle, and the storage directories were reworked (requires serious_python >= 4.0.0, now pinned in the flet build template). Your Python sources ship unpacked inside the app bundle next to the stdlib/site-packages (no first-launch app.zip extraction) on macOS/iOS/Windows/Linux; on Android they ship as a stored app.zip asset unpacked once on first launch; web is unchanged. The app directory is now read-only, so the Python program's working directory moved to a writable, app-private data dir. FLET_APP_STORAGE_DATA now maps to the OS application support dir (a data subdir) instead of the user's Documents folder and is the cwd; FLET_APP_STORAGE_TEMP now points to the OS temp dir (was the cache dir) and a new FLET_APP_STORAGE_CACHE exposes the cache dir. flet run sets the dev cwd to a hidden, git-ignored <project>/.flet/storage/data. Relative reads of bundled files (open("seed.json")) must move to __file__/importlib.resources or assets/. See the app files unpacked / storage dirs guide by @FeodorFitsner.
  • flet build and flet publish now bundle CPython 3.14 by default (previously 3.12, implicit via the old single-version serious_python). Existing apps that depend on native wheels without 3.14 binaries should pin explicitly with --python-version 3.12 (CLI), requires-python = ">=3.12,<3.13" (pyproject), or SERIOUS_PYTHON_VERSION=3.12 in the build environment (#6577) by @FeodorFitsner.
  • Android builds now include only the ABIs the bundled Python supports, sourced per-version from python-build's manifest (pythons.<short>.android_abis) rather than hardcoded in flet. As of python-build 20260630, armeabi-v7a (32-bit ARM) is published for 3.12, 3.13 and 3.14, so all three build it by default; an explicit --arch <abi> is validated against the selected Python's supported set and fails with a clear error otherwise (#6578) by @ndonkoHenri.
  • flet build / flet publish now compile your app and packages to .pyc by default (previously off). This removes per-launch bytecode recompilation — a significant cold-start win, especially on mobile where pure Python is imported from a stored zip (zipimport) and can't cache bytecode back to disk, so every module would otherwise recompile from source on each launch. The CLI flags gain --no-compile-app / --no-compile-packages (via argparse.BooleanOptionalAction; the existing --compile-app / --compile-packages still work), and [tool.flet.compile].app / .packages now default to true. Pass --no-compile-* or set them to false to restore the old behavior (faster iterative builds, or keeping .py source in the bundle). Compiled web builds were verified to load in Pyodide (bundled CPython and Pyodide share the same minor version). See the compile-on-by-default guide (#6598) by @FeodorFitsner.
  • Flet protocol wire format on stream-oriented transports (UDS / TCP) is incompatible with pre-0.86 servers and clients. Every packet now starts with a 4-byte little-endian length prefix and a 1-byte type discriminator (0x00 = MsgPack control frame, 0x01 = raw DataChannel frame). WebSocket / postMessage / dart_bridge transports keep native message boundaries and only gain the type byte. The Flet CLI dev server and the in-process Python runtime are upgraded in lockstep — running flet run with mismatched flet versions across CLI and runtime is no longer supported. See the DataChannel protocol framing upgrade guide. The MatplotlibChartCanvas widget transports its full / diff / clear frames via a DataChannel rather than _invoke_method arguments — visually identical, but custom code that subclassed it and overrode the apply methods may need updating by @FeodorFitsner.

Deprecations

  • Deprecate the --clear-cache flag of flet build and flet debug; use the new flet clean command instead. The flag remains functional but now emits a deprecation warning, and is scheduled for removal in 0.89.0 (#6233) by @ndonkoHenri.

Bug fixes

  • Fix a debug-mode '!_dirty': is not true assertion (EXCEPTION CAUGHT BY WIDGETS LIBRARY in _BootOverlay) thrown by apps built or debugged from the flet build template when the app becomes ready. With the default boot_screen.fade_out_duration of 0 the overlay's zero-duration AnimatedOpacity completed synchronously, firing onEnd — and its setState — in the middle of the overlay's own rebuild. The overlay is now removed in a single state update when no fade is configured; non-zero fade durations still animate as before. Debug-mode only: the assertion is compiled out of release builds (#6666) by @FeodorFitsner.
  • Fix flet build failing on Windows when a dependency is pulled in via [tool.flet.<platform>].dev_packages (or any local-path install): the rewritten <pkg> @ file://<path> URL now uses Path.as_uri(), producing the correct file:///D:/... three-slash form instead of file://D:\..., which pip on Windows parsed as a UNC path and aborted with OSError: [Errno 2] No such file or directory: '\\\\D:\\a\\...' (#6577) by @FeodorFitsner.
  • Fix flet build web --python-version 3.13 failing to match any Pyodide-built native wheel. The 3.13 row in the Python version registry was set to Pyodide platform tag pyodide-2025.0-wasm32, but Pyodide actually publishes 0.29 wheels under pyemscripten_2025_0_wasm32 (the pyodide_pyemscripten_ prefix transition happened at 0.28/0.29, not at 314.0). Corrected to pyemscripten-2025.0-wasm32 so pip's wheel selection picks up the correct tags by @FeodorFitsner.
  • flet build now cleans the build directory when the bundled Python version changes between builds, preventing stale compiled bytecode from the previous version crashing the app at runtime with ImportError: bad magic number by @FeodorFitsner.
  • Fix locating Flet controls by their user-assigned key in tests. ValueKey(control.key) was constructed as ValueKey<Object>, and Flutter's runtimeType-strict ValueKey.== never matches that against the ValueKey<String> the rendered widget carries — so find.byKey(Key('foo')) (flutter_test) and find_by_key('foo') (Flet tester) located 0 widgets. The ValueKey is now built with the value's concrete type (String → ValueKey<String>, int → ValueKey<int>, …) on both the Dart and Python sides by @FeodorFitsner.
  • Fix flet build apk / flet build aab with --arch packaging native libraries for all Android ABIs instead of only the requested ones. The requested architectures are now forwarded to Flutter as --target-platform (so --split-per-abi builds only the requested splits), unrequested ABI directories are excluded from the artifact via packaging.jniLibs.excludes, Android --arch values are validated against the bundled Python's supported ABIs, multiple --arch values now correctly reach serious_python (comma-joined), and stale artifacts from previous builds are no longer copied into the output directory (#6567, #6578) by @ndonkoHenri.
  • Fix repeated --arch, --source-packages and --permissions flags in flet build keeping only the values of the last occurrence (action="extend" on each) (#6578) by @ndonkoHenri.
  • Fix flet build apk failing at mergeDebugNativeLibs with N files found with path 'lib/<abi>/libc++_shared.so' when an app combines serious_python_android with another Flutter plugin that also bundles the NDK C++ runtime (#6570, #6571) by @ndonkoHenri.
  • Specify handler signatures in subscribe and subscribe_topic methods of PubSubClient for better type checking (#6549) by @Iaw4tch
  • Fix FilePicker.pick_files() on web for slow network shares or slow machines: pass cancel_upload_on_window_blur=False to prevent valid file selections from being reported as cancelled when the browser window loses focus during file picking (#771, #6573) by @ndonkoHenri.
  • Support PagePlatform.ANDROID_TV in Page.get_device_info() retrieval (#6604) by @bl1nch.
  • Fix ProgressRing.year_2023 being ignored, so the control correctly switches between the latest and 2023 Material Design appearances (#6614) by @ndonkoHenri.
  • flet build ipa / ios apps that ship ctypes packages with plain .dylib shared libraries (e.g. llama-cpp-python) now load them on the iOS simulator instead of failing at launch with a dlopen platform mismatch (have 'iOS', need 'iOS-simulator'); the iOS runtime also now bundles the _multiprocessing extension (importable, not spawnable). Bumps the pinned bundle to serious_python 4.2.1 / python-build 20260701 (serious_python#223) by @ndonkoHenri, @FeodorFitsner.
  • Improve performance of checking added/removed controls in Session.patch_control from O(N²) to O(N) (#6651) by @davidlawson.
  • Fix stateful controls inside ResponsiveRow (video players, WebViews, scroll positions) losing their state whenever a window resize crossed a breakpoint and the layout switched between a single row and wrapping (#6661, #6663) by @FeodorFitsner.

Documentation

  • Improve FilePicker.save_file() documentation: on desktop, passing src_bytes writes those bytes to the selected file (#6573) by @ndonkoHenri.

New Contributors

Full Changelog: v0.85.3...v0.86.0

Don't miss a new flet release

NewReleases is sending notifications on new releases.