Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Fix `find_platform_image` selecting incompatible icon formats (e.g. `.icns` on Windows) by ranking glob results per target platform ([#6381](https://github.com/flet-dev/flet/pull/6381)) by @HG-ha.
* Fix `Page.show_drawer()`, `close_drawer()`, and root/top view accessors (`appbar`, `drawer`, `navigation_bar`, `controls`, ...) failing with `TypeError` under `Page.render_views()` by unwrapping component-wrapped views and normalizing single-view returns ([#6413](https://github.com/flet-dev/flet/issues/6413), [#6414](https://github.com/flet-dev/flet/pull/6414)) by @FeodorFitsner.
* Fix `auto_scroll` on scrollable controls silently doing nothing unless `scroll` was also explicitly set ([#6397](https://github.com/flet-dev/flet/issues/6397), [#6404](https://github.com/flet-dev/flet/pull/6404)) by @ndonkoHenri.
* Fix `flet pack` desktop packaging so Windows and Linux bundles include the expected client archive, and Windows taskbar pins point to the packed app instead of the cached `flet.exe` ([#5151](https://github.com/flet-dev/flet/issues/5151), [#6403](https://github.com/flet-dev/flet/pull/6403)) by @ndonkoHenri.

### Other changes

Expand Down
13 changes: 13 additions & 0 deletions client/windows/runner/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <shobjidl.h>
#include <windows.h>

#include "flutter_window.h"
Expand All @@ -17,6 +18,18 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
// plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

// Set AppUserModelID so that Windows associates this window with the
// parent application (e.g. a PyInstaller-packaged exe) rather than
// this flet.exe binary. This ensures taskbar pins and shortcuts
// point to the correct executable.
wchar_t *aumid = nullptr;
size_t aumid_len = 0;
if (_wdupenv_s(&aumid, &aumid_len, L"FLET_APP_USER_MODEL_ID") == 0 &&
aumid != nullptr && aumid[0] != L'\0') {
::SetCurrentProcessExplicitAppUserModelID(aumid);
}
free(aumid);

flutter::DartProject project(L"data");

std::vector<std::string> command_line_arguments =
Expand Down
1 change: 1 addition & 0 deletions packages/flet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Bug fixes

* Handle unbounded width in `ResponsiveRow` with an explicit error and treat child controls with `col=0` as hidden at the current breakpoint ([#1951](https://github.com/flet-dev/flet/issues/1951), [#3805](https://github.com/flet-dev/flet/issues/3805), [#6354](https://github.com/flet-dev/flet/pull/6354)) by @ndonkoHenri.
* Fix `flet pack` desktop packaging so Windows and Linux bundles include the expected client archive, and Windows taskbar pins point to the packed app instead of the cached `flet.exe` ([#5151](https://github.com/flet-dev/flet/issues/5151), [#6403](https://github.com/flet-dev/flet/pull/6403)) by @ndonkoHenri.

### Other changes

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import logging
import os
import sys

logger = logging.getLogger("flet")


logger.info("Running PyInstaller runtime hook for Flet...")

os.environ["FLET_SERVER_IP"] = "127.0.0.1"

# On Windows, set AppUserModelID so the taskbar associates the Flet client window
# with the parent executable (a PyInstaller bundle in this case) rather than the cached
# flet.exe. This ensures taskbar pins and shortcuts point to the correct executable.
if sys.platform == "win32" and "FLET_APP_USER_MODEL_ID" not in os.environ:
os.environ["FLET_APP_USER_MODEL_ID"] = os.path.abspath(sys.executable)
54 changes: 48 additions & 6 deletions sdk/python/packages/flet-cli/src/flet_cli/commands/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import os
import shutil
import sys
import tarfile
import zipfile
from pathlib import Path

import flet_cli.__pyinstaller.config as hook_config
from flet.utils import is_macos, is_windows
from flet.utils import is_linux, is_macos, is_windows
from flet_cli.commands.base import BaseCommand


Expand Down Expand Up @@ -152,6 +154,31 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
help="Enable non-interactive mode. All prompts will be skipped",
)

def compress_flet_client_dir(self, temp_bin_dir: str, archive_name: str) -> None:
"""Compress the flet/ directory into an archive and remove the original.

Args:
temp_bin_dir: Path to the temporary directory containing the flet/
subdirectory with client binaries.
archive_name: Target archive filename. Uses zip for `.zip`
extensions and gzipped tar for everything else.
"""
flet_dir = os.path.join(temp_bin_dir, "flet")
if not os.path.isdir(flet_dir):
return
archive_path = os.path.join(temp_bin_dir, archive_name)
if archive_name.endswith(".zip"): # windows
with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as zf:
for root, _dirs, files in os.walk(flet_dir):
for f in files:
full = os.path.join(root, f)
arcname = os.path.relpath(full, temp_bin_dir)
zf.write(full, arcname)
else:
with tarfile.open(archive_path, "w:gz") as tar:
tar.add(flet_dir, arcname="flet")
shutil.rmtree(flet_dir)

def handle(self, options: argparse.Namespace) -> None:
"""
Package the app into a standalone desktop artifact.
Expand Down Expand Up @@ -293,6 +320,12 @@ def handle(self, options: argparse.Namespace) -> None:

pyi_args.extend(["--version-file", version_info_path])

# Compress the patched flet/ directory into flet-windows.zip
# so ensure_client_cached() finds it at runtime.
self.compress_flet_client_dir(
hook_config.temp_bin_dir, "flet-windows.zip"
)

elif is_macos():
from flet_cli.__pyinstaller.macos_utils import (
assemble_app_bundle,
Expand Down Expand Up @@ -341,12 +374,12 @@ def handle(self, options: argparse.Namespace) -> None:
copyright=options.copyright,
)

# assemble
# Compress the patched .app bundle back into flet-macos.tar.gz so
# ensure_client_cached() finds it at runtime.
assemble_app_bundle(app_path, tar_path)

# Remove everything except the tar.gz so
# PyInstaller doesn't try to process loose
# framework binaries.
# Remove everything except the tar.gz so PyInstaller doesn't try
# to process loose framework binaries.
for entry in os.listdir(hook_config.temp_bin_dir):
entry_path = os.path.join(hook_config.temp_bin_dir, entry)
if entry_path == tar_path:
Expand All @@ -356,7 +389,16 @@ def handle(self, options: argparse.Namespace) -> None:
else:
os.remove(entry_path)

# run PyInstaller!
elif is_linux():
from flet_desktop import get_artifact_filename

# Compress the flet/ directory into a tar.gz
# so ensure_client_cached() finds it at runtime.
self.compress_flet_client_dir(
hook_config.temp_bin_dir, get_artifact_filename()
)

# run PyInstaller
print("Running PyInstaller:", pyi_args)
PyInstaller.__main__.run(pyi_args)

Expand Down
47 changes: 23 additions & 24 deletions sdk/python/packages/flet-desktop/src/flet_desktop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ def get_package_bin_dir():

def __get_desktop_flavor():
"""
Return the desktop client flavor to use: ``"full"`` or ``"light"``.
Return the desktop client flavor to use: `"full"` or `"light"`.

Resolution order:

1. ``FLET_DESKTOP_FLAVOR`` environment variable.
2. ``[tool.flet].desktop_flavor`` in the project's ``pyproject.toml``.
3. Default: ``"light"`` on Linux, ``"full"`` elsewhere.
1. `FLET_DESKTOP_FLAVOR` environment variable.
2. `[tool.flet].desktop_flavor` in the project's `pyproject.toml`.
3. Default: `"light"` on Linux, `"full"` elsewhere.
"""

env_flavor = os.environ.get("FLET_DESKTOP_FLAVOR", "").strip().lower()
Expand Down Expand Up @@ -93,9 +93,9 @@ def __get_desktop_flavor():

def __get_system_glibc_version():
"""
Return the system glibc version as a ``(major, minor)`` tuple.
Return the system glibc version as a `(major, minor)` tuple.

Falls back to ``(0, 0)`` when detection fails.
Falls back to `(0, 0)` when detection fails.
"""

try:
Expand All @@ -118,7 +118,7 @@ def __get_linux_distro_id():

Uses glibc version detection to pick the best matching build target
(highest glibc requirement that is <= system glibc). Can be
overridden via the ``FLET_LINUX_DISTRO`` environment variable.
overridden via the `FLET_LINUX_DISTRO` environment variable.
"""

override = os.environ.get("FLET_LINUX_DISTRO", "").strip()
Expand All @@ -139,13 +139,13 @@ def __get_linux_distro_id():
return best


def __get_artifact_filename():
def get_artifact_filename():
"""
Return the release artifact filename for the current platform.

Windows: ``flet-windows.zip``
macOS: ``flet-macos.tar.gz``
Linux: ``flet-linux-{distro}[-light]-{arch}.tar.gz``
Windows: `flet-windows.zip`
macOS: `flet-macos.tar.gz`
Linux: `flet-linux-{distro}[-light]-{arch}.tar.gz`
"""

if is_windows():
Expand All @@ -165,8 +165,7 @@ def __get_client_storage_dir():
"""
Return a versioned local directory used to store unpacked desktop client files.

The path format is:
``~/.flet/client/flet-desktop-{flavor}-{version}``.
The path format is: `~/.flet/client/flet-desktop-{flavor}-{version}`.
"""

flavor = __get_desktop_flavor()
Expand All @@ -180,11 +179,11 @@ def __download_flet_client(file_name):
Download a Flet client archive from GitHub Releases.

The download URL is constructed from the version embedded in
``flet_desktop.version.version``. It can be overridden entirely
via the ``FLET_CLIENT_URL`` environment variable.
`flet_desktop.version.version`. It can be overridden entirely
via the `FLET_CLIENT_URL` environment variable.

Args:
file_name: Archive filename to download (e.g. ``flet-macos.tar.gz``).
file_name: Archive filename to download (e.g. `flet-macos.tar.gz`).

Returns:
Local path to the downloaded archive.
Expand Down Expand Up @@ -217,7 +216,7 @@ def ensure_client_cached():
logger.info(f"Flet client found in cache: {cache_dir}")
return cache_dir

artifact = __get_artifact_filename()
artifact = get_artifact_filename()

# Check for a bundled archive (PyInstaller or legacy wheel).
bundled = os.path.join(get_package_bin_dir(), artifact)
Expand Down Expand Up @@ -327,22 +326,22 @@ def __locate_and_unpack_flet_view(page_url, assets_dir, hidden):

Resolution strategy (per platform):

1. Prefer app binaries produced by ``flet build`` in the current workspace.
2. Use ``FLET_VIEW_PATH`` when provided.
3. Use cached / downloaded client from ``~/.flet/client/``.
1. Prefer app binaries produced by `flet build` in the current workspace.
2. Use `FLET_VIEW_PATH` when provided.
3. Use cached / downloaded client from `~/.flet/client/`.

Platform-specific launch commands are prepared for Windows, macOS, and Linux.

Args:
page_url: Page endpoint the desktop client should open.
assets_dir: Optional assets directory passed to the client process.
hidden: Whether to set ``FLET_HIDE_WINDOW_ON_START=true`` in process env.
hidden: Whether to set `FLET_HIDE_WINDOW_ON_START=true` in process env.

Returns:
A tuple containing:
- ``list[str]``: command arguments for the desktop client.
- ``dict[str, str]``: environment variables for the launched process.
- ``str``: path to the temporary PID file.
- `list[str]`: command arguments for the desktop client.
- `dict[str, str]`: environment variables for the launched process.
- `str`: path to the temporary PID file.

Raises:
FileNotFoundError: If a required desktop executable or archive
Expand Down
8 changes: 8 additions & 0 deletions website/docs/reference/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ It is already pre-created and its location depends on the platform the app is ru
A directory for the storage of temporary application files, i.e. cache.
It is already pre-created and its location depends on the platform the app is running on.

### `FLET_APP_USER_MODEL_ID`

Windows [AppUserModelID](https://learn.microsoft.com/en-us/windows/win32/shell/appids)
used by the desktop client process for taskbar grouping and pinning.

For apps packaged with [`flet pack`](../cli/flet-pack.md), this value is set automatically
so taskbar pins point to the packaged app executable instead of the cached Flet client executable.

### `FLET_ASSETS_DIR`

Absolute path to the app's assets directory.
Expand Down
Loading