fix(web): make the client Flet actually serves visible and overridable - #6835
Merged
Merged
Conversation
`flet run --web`, `ft.run(export_asgi_app=True)` and `flet publish` all serve the prebuilt web client from the `flet-web` package. Its extension list is fixed when Flet is released, so a third-party control renders as `Unknown control: <Type>` in those modes no matter how the app was built — the reason extensions appear to work on desktop (where `flet run` prefers a client from a previous `flet build`) and vanish in the browser. `FLET_WEB_PATH` already pointed the server at a different web root, but aiming it at a `flet build web` output did not work: that page bakes `flet.pyodide = true` into its config literal and shipped no `<!-- fletAppConfig -->` marker, so the server could inject nothing and the client kept trying to start Python in the browser instead of connecting over the socket. The build template now carries the marker, and `patch_index_html` writes the Pyodide flag either way rather than relying on the page's own default. A client generated for one mode can therefore be served in the other. Verified end to end: `flet build web` on an app using a third-party extension, then `FLET_WEB_PATH=build/web flet run --web` — the extension control renders, with host Python over the socket and no Pyodide worker. Both existing paths are unchanged: the prebuilt client is still served with the flag off, `flet publish` still with it on.
Both point Flet at a client you supply instead of the one it downloads or bundles, and neither appeared anywhere in the docs. `FLET_WEB_PATH` is the one worth finding: pointing it at a `flet build web` output is how a third-party extension renders in `flet run --web`, since extension Dart code is compiled into the client and the bundled one is built when Flet is released. `FLET_VIEW_PATH` gets its resolution order spelled out — `flet build` output, then this variable, then the cached client — because which client actually runs is otherwise guesswork. [skip ci]
`-v` was accepted and advertised as "enable verbose output", but `run.py` never read `options.verbose` — nothing anywhere mapped the flag to a log level. So `flet run --web -v` printed nothing, and the 26 `logger.info`/`logger.debug` calls in the web transport were unreachable unless the app configured logging itself. `flet run` now passes the level to the app it starts through `FLET_LOG_LEVEL` (`-v` info, `-vv` debug), and `ft.run()` applies it. `basicConfig` is a no-op once the root logger has a handler, so an app that sets up logging keeps what it chose. The web root also names `FLET_WEB_PATH` when that is where it came from. The desktop client already reports this for `FLET_VIEW_PATH`, and without it the line gives a path with no indication of which source won. Measured on a web app: no flag 0 lines (unchanged), `-v` 19 lines, `-vv` 86 lines of which 67 debug, and an app calling `basicConfig(level=ERROR)` still prints neither.
… bundle On macOS the variable was trusted without checking: the path went straight into `os.listdir`, so a stale or emptied developer client produced a bare `FileNotFoundError` from a daemon thread — surfacing as "Exception in thread Thread-4 (open_flet_view_and_wait)" and ending the run. Windows and Linux already check their executable and warn, then fall through to the cached client; macOS now does the same through a shared `find_macos_app_bundle` helper, which also replaces the hand-rolled scan of `build/macos`. `flet run` also configures logging for its own process, not only for the app it starts. `FLET_LOG_LEVEL` reaches the child, but the desktop client is launched from the CLI process, so with `-v` the app talked while the launcher stayed silent about which client it picked — the one thing worth knowing when the wrong one loads. Verified on macOS: FLET_VIEW_PATH at an empty directory now logs "FLET_VIEW_PATH set to ... but no .app bundle found there" and moves on, and a valid one logs the bundle it took and starts the session.
[skip ci]
ndonkoHenri
requested review from
FeodorFitsner
and
a lite review from Copilot
September 11, 2026 16:31
Deploying flet-website-v2 with
|
| Latest commit: |
b8cf74a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ae18256a.flet-website-v2.pages.dev |
| Branch Preview URL: | https://fix-web-path-built-client.flet-website-v2.pages.dev |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Three unresolved moderate findings remain concerning relative FLET_WEB_PATH resolution and stale published app archive URLs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Updates Flet’s web-client selection, logging, macOS fallback behavior, and related documentation.
Changes:
- Supports built web clients through
FLET_WEB_PATHwith explicit Pyodide configuration. - Enables
-v/-vvlogging and propagatesFLET_LOG_LEVEL. - Improves macOS client fallback handling and documents the new environment variables.
File summaries
| File | Reviewed changes |
|---|---|
website/docs/reference/environment-variables.md |
Documents client-path and logging environment variables. |
website/docs/cookbook/logging.md |
Documents verbose run logging. |
sdk/python/templates/build/{{cookiecutter.out_dir}}/web/index.html |
Adds the runtime configuration marker. |
sdk/python/packages/flet/src/flet/app.py |
Applies FLET_LOG_LEVEL. |
sdk/python/packages/flet-web/src/flet_web/patch_index.py |
Configures Pyodide mode. Moderate, 2 votes: published pages may retain a stale app archive URL. |
sdk/python/packages/flet-web/src/flet_web/fastapi/flet_static_files.py |
Handles custom web-root selection. Moderate, 2 votes: relative FLET_WEB_PATH resolves from the child process directory. |
sdk/python/packages/flet-desktop/src/flet_desktop/__init__.py |
Safely handles missing macOS client bundles. |
sdk/python/packages/flet-cli/src/flet_cli/commands/run.py |
Configures and propagates verbose logging. Moderate, 1 vote: relative FLET_WEB_PATH fails after changing the child working directory. |
CHANGELOG.md |
Records the user-facing fixes. |
Review details
Suppressed comments (1)
sdk/python/packages/flet-cli/src/flet_cli/commands/run.py:467
Handler.start_process()launches the child withcwd=self.flet_app_data_dir, but this inheritedFLET_WEB_PATHis passed through unchanged. Therefore the documented/claimed commandFLET_WEB_PATH=build/web flet run --webresolves relative to.flet/storage/dataand fails to find the project’sbuild/web; resolve relative web paths before spawning (or explicitly require an absolute path).
if self.verbose > 0:
p_env["FLET_LOG_LEVEL"] = "debug" if self.verbose > 1 else "info"
- Files reviewed: 9/9 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…URL current Two problems with pointing FLET_WEB_PATH at a `flet build web` output, both raised by Copilot on #6835. `flet run` starts the app with its cwd set to the storage directory, so `FLET_WEB_PATH=build/web` — the form the docs give — resolved under storage and failed with "Web root path not found". The path is now resolved before the child is spawned, while the directory the user typed it from is still the meaning. `flet publish` writes the app as `app.tar.gz`, but a built page names the archive it was generated with, `assets/app/app.zip`. Publishing through such a client therefore pointed the page at the stale build archive. `patch_index_html` now writes the archive URL for the caller that knows it, alongside the Pyodide flag it already states explicitly. Verified: `FLET_WEB_PATH=build/web flet run --web` serves the built client; a built page patched for publish reports `app.tar.gz` while the prebuilt client is unaffected, and neither URL is injected in server mode, where it means nothing.
FeodorFitsner
approved these changes
Sep 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four related fixes that came out of chasing why a third-party extension renders as
Unknown controlin the browser but works on desktop.1.
FLET_WEB_PATHnow works with aflet build weboutputflet run --web,ft.run(export_asgi_app=True)andflet publishserve the prebuilt web client from theflet-webpackage. Its extension list is fixed when Flet is released, so a third-party control renders asUnknown control: <Type>there however the app was built — the reason extensions work on desktop (whereflet runprefers a client from a previousflet build) and vanish on web.FLET_WEB_PATHalready pointed the server at another web root, but aiming it at a built app did not work: that page bakesflet.pyodide = trueinto its config and shipped no<!-- fletAppConfig -->marker, so the server could inject nothing and the client kept trying to start Python in the browser.The build template now carries the marker, and
patch_index_htmlwrites the Pyodide flag either way instead of relying on the page's own default.Verified:
flet build webon an app using a third-party extension, thenFLET_WEB_PATH=build/web flet run --web— the extension renders, host Python over the socket, no Pyodide worker. Both existing paths are unchanged (prebuilt client served with the flag off,flet publishwith it on).Deliberately not included: auto-detecting
./build/webthe way desktop auto-detectsbuild/<platform>. On web that would silently reinterpret a Pyodide artifact as a server-mode client, and the same directory is separately runnable viaflet serve— so someone could reasonably believeflet run --webis testing what they would deploy. Worth proposing on its own.2.
flet run -venables logging-vwas accepted and advertised as "enable verbose output", butrun.pynever readoptions.verboseand nothing mapped it to a log level.flet run --web -vprinted nothing at all, leaving everylogger.infoin the web transport unreachable.flet runnow configures logging for its own process and passes the level to the app it starts through a newFLET_LOG_LEVEL(-vinfo,-vvdebug). Both halves are needed: the app runs in a subprocess, but the desktop client is launched from the CLI process.basicConfigis a no-op once the root logger has a handler, so an app that configures logging keeps what it chose.-v-vv-v, app setsERROR3. macOS no longer crashes on a stale
FLET_VIEW_PATHThe path went straight into
os.listdir, so a stale or emptied developer client raised a bareFileNotFoundErrorfrom a daemon thread — surfacing asException in thread Thread-4 (open_flet_view_and_wait)and ending the run. Windows and Linux already check and warn, then fall through to the cached client. macOS now does the same through a sharedfind_macos_app_bundle, which also replaces the hand-rolled scan ofbuild/macos.The web root also names
FLET_WEB_PATHwhen that is where it came from, matching what desktop reports forFLET_VIEW_PATH.4. Both variables are documented
FLET_VIEW_PATHandFLET_WEB_PATHappeared nowhere in the docs. Added to the environment variables reference withFLET_VIEW_PATH's resolution order spelled out —flet buildoutput, then the variable, then the cached client — since which client actually runs is otherwise guesswork.FLET_LOG_LEVELadded too, plus a note in the logging cookbook thatflet run -vsets it, which lets someone produce logs for an issue report without editing their app.Summary by Sourcery
Make web client selection overridable, expose useful verbose logging, and harden desktop client discovery.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests: