Skip to content

Run embedded FletApps over the in-process dart_bridge transport - #6723

Merged
FeodorFitsner merged 4 commits into
mainfrom
flet-app-dart-bridge
Jul 26, 2026
Merged

Run embedded FletApps over the in-process dart_bridge transport#6723
FeodorFitsner merged 4 commits into
mainfrom
flet-app-dart-bridge

Conversation

@FeodorFitsner

@FeodorFitsner FeodorFitsner commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Lets an embedded FletApp — a Flet program hosted inside another Flet app (a gallery, a preview) — run over the in-process dart_bridge transport instead of a socket. Also pins serious_python 4.4.0 / python-build 20260725.

Why

An embedded app could only reach its host over a socket. On mobile that's a Unix domain socket, whose path is capped at ~104 bytes (sun_path) — on the iOS simulator the container path overflows it and every embedded app dies with AF_UNIX path too long. It's also needlessly slow for two programs sharing one process.

dart_bridge is the same in-process transport the root app already uses, and it's already wired up in every flet build output.

The rendezvous

dart_bridge channels are keyed by a Dart-allocated native port, so the flow inverts compared to sockets (where Python creates the socket and hands over a path):

  1. FletAppControl sees url="dartbridge://" and asks the registered connector for a channel.
  2. It hands the allocated port to the host's Python through a new FletApp.on_connect event.
  3. The host serves that port with a FletDartBridgeServer.

The channel's existing send-retry loop covers the window before Python registers its handler, so there's no ordering requirement between the two sides.

This mirrors how DataChannel already propagates a Dart-allocated port to Python (RawImage fires data_channel_open with channel_id) — same pattern, applied to a whole embedded app.

Keeping flet free of serious_python

The flet package can't depend on serious_python (it has to compile for web, where dart:ffi doesn't exist). So flet exposes a process-global hook:

EmbeddedDartBridgeConnector? embeddedDartBridgeConnector;

The build template's native_runtime.dart — which already owns PythonBridge — registers it in initBridges(), reusing the _DartBridgeBackendChannel and _PythonBridgeDataChannelFactory already defined there. Each embedded app mints its own PythonBridge, and high-throughput DataChannels (RawImage, MatplotlibChart) still get dedicated bridges.

Opt-in and backwards compatible

Without the dartbridge:// scheme — or anywhere dart_bridge doesn't exist (web, desktop dev) — the connector is null and FletApp uses the URL-scheme channel factory exactly as before, so hosts can fall back to a socket URL. Existing embedded apps are unaffected.

Also in this PR

Pins serious_python 4.4.0 and PYTHON_BUILD_RELEASE_DATE 20260725 (they must reference the same python-build release). 4.4.0 ships dart_bridge as a dynamic framework, fixing iOS release builds crashing at startup with Failed to lookup symbol 'serious_python_run' — see flet-dev/serious-python#240, flet-dev/dart-bridge#11 and #12. Bundled Python versions are unchanged (3.12.13 / 3.13.14 / 3.14.6).

Verification

Validated end to end on a real iPhone with Flet Studio's gallery, which hosts each example as an embedded FletApp:

  • Examples run over dart_bridge; the AF_UNIX path too long failure is gone.
  • The bridge benchmark in serious_python's CI reports up to ~2.6 GB/s on this transport.
  • A matplotlib example exercises the dedicated-DataChannel path.
  • Open → back → reopen leaves no leaked bridge (the connector's dispose() runs on unmount).
  • Desktop flet run, where dart_bridge is absent, still runs the same examples over the socket fallback.

flutter analyze clean.

Summary by Sourcery

Enable embedded Flet apps to use an in-process dart_bridge transport via a new connector and event-based rendezvous, while keeping socket-based communication as a fallback where dart_bridge is unavailable.

New Features:

  • Allow embedded FletApp instances to run over an in-process dart_bridge channel when addressed with the dartbridge:// URL scheme.
  • Introduce a process-global EmbeddedDartBridgeConnector and EmbeddedDartBridge abstraction to allocate per-embedded-app Dart↔Python bridges.
  • Add a FletApp.on_connect event to notify hosts of the Dart-allocated native port for serving an embedded app via FletDartBridgeServer.

Bug Fixes:

  • Bump serious_python to 4.4.0 and align PYTHON_BUILD_RELEASE_DATE to 20260725 to fix iOS release builds crashing at startup due to missing dart_bridge symbols.

Enhancements:

  • Wire the native build runtime to register the EmbeddedDartBridgeConnector using PythonBridge and existing dart_bridge backend channel infrastructure.
  • Export the embedded_dart_bridge.dart API from the flet package for use by embedded apps and hosts.

Build:

  • Update build templates and pubspec to depend on serious_python 4.4.0 and the corresponding python-build release date.

Documentation:

  • Document the new embedded dart_bridge transport, on_connect event, and iOS crash fix in the changelog.

Updates the FVM-pinned Flutter SDK from 3.44.7 to 3.44.8 so local and CI environments use the newer patch release consistently.
Fixes iOS apps built with 'flet build ipa' crashing at startup with
'Failed to lookup symbol serious_python_run: dlsym(RTLD_DEFAULT,
serious_python_run): symbol not found'.

serious_python shipped dart_bridge -- the in-process Dart<->Python
transport -- as a static library linked into the app executable. An iOS
executable exports nothing to the dynamic symbol table by default and the
release build strips local symbols, so the dlsym lookups Dart
(DynamicLibrary.process()) and Python ('import dart_bridge') perform at
runtime could not resolve. Only release/archive device builds under the
Swift Package Manager path were affected: debug/simulator don't
dead-strip, and Android was never affected (its dart_bridge is a dynamic
.so that exports its symbols).

serious_python 4.4.0 ships dart_bridge as a dynamic framework, embedded
and signed into the app like Python.xcframework with its symbols
exported. Its bundled python-build snapshot moves to 20260725
(dart_bridge 1.5.1 -> 1.6.1, Pyodide 3.14 314.0.2 -> 314.0.3), so
PYTHON_BUILD_RELEASE_DATE is re-pinned to match -- the two must reference
the same python-build release. Bundled Python versions (3.12.13 /
3.13.14 / 3.14.6) are unchanged.

Verified the pinned manifest resolves: 3.12.13 / 3.13.14 / 3.14.6 with
Pyodide 314.0.3 on 3.14.
An embedded FletApp (a Flet program hosted inside another Flet app -- a
gallery, a preview) could only reach its host over a socket. On mobile that
means a Unix domain socket, whose path is capped at ~104 bytes: on the iOS
simulator the container path overflows it and every embedded app fails with
"AF_UNIX path too long". It is also needlessly slow for two programs sharing
one process.

Embedded apps can now use dart_bridge, the same in-process transport the
root app already uses. dart_bridge channels are keyed by a Dart-allocated
native port, so the rendezvous is inverted compared to the socket flow:

  - FletAppControl sees url="dartbridge://", asks the registered connector
    for a channel, and hands the allocated port to the host's Python via a
    new FletApp "connect" control event.
  - The host serves that port with a FletDartBridgeServer. The channel's
    existing send-retry loop covers the window before the handler registers.

The flet package stays free of any serious_python dependency: it exposes a
process-global embeddedDartBridgeConnector hook that the build template's
native_runtime.dart registers, reusing the PythonBridge-backed channel and
data-channel factory already defined there. Each embedded app mints its own
PythonBridge, and high-throughput DataChannels (RawImage, MatplotlibChart)
get dedicated bridges as before.

Opt-in and backwards compatible: without the scheme, or where dart_bridge
does not exist (web, desktop dev), FletApp keeps using the URL-scheme
channel factory, so hosts can fall back to a socket URL.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying flet-website-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 042dca5
Status: ✅  Deploy successful!
Preview URL: https://63210043.flet-website-v2.pages.dev
Branch Preview URL: https://flet-app-dart-bridge.flet-website-v2.pages.dev

View logs

Updates the build template to use `serious_python` 4.4.1 and advances the pinned python-build release date from `20260725` to `20260726` in the CLI. The changelog entry was adjusted to match the new snapshot date for the iOS startup crash fix notes.
@FeodorFitsner
FeodorFitsner merged commit a5f12d8 into main Jul 26, 2026
2 of 176 checks passed
@FeodorFitsner
FeodorFitsner deleted the flet-app-dart-bridge branch July 26, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant