From 9564c77feee716434af6d086d87c8240a7f13ca1 Mon Sep 17 00:00:00 2001 From: HG-ha <1790233968@qq.com> Date: Sun, 5 Apr 2026 20:12:20 +0800 Subject: [PATCH 1/6] fix: select platform-compatible icon format in find_platform_image find_platform_image uses glob.glob to match icon files by base name, then blindly picks images[0]. On NTFS the alphabetical ordering causes .icns (macOS-only) to be selected over .ico/.png when building for Windows, which makes flutter_launcher_icons fail with NoDecoderForImageFormatException. Introduce a per-platform preference list so the method picks the most appropriate format for the target: .ico first on Windows, .icns first on macOS, .png as universal fallback everywhere. Made-with: Cursor --- .../src/flet_cli/commands/build_base.py | 59 +++++++++++++++---- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py index 549aa34670..8a0fa819de 100644 --- a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py +++ b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py @@ -2263,6 +2263,20 @@ def rename_android_build_outputs(self): style=verbose1_style, ) + # Preferred icon extensions per target platform. Earlier entries win. + # .png is a universal fallback supported by flutter_launcher_icons on + # every platform, while .icns is macOS-only and .ico is Windows-only. + _PLATFORM_IMAGE_PREFERENCE: dict[str, list[str]] = { + "windows": [".ico", ".png", ".jpg", ".jpeg", ".bmp"], + "macos": [".icns", ".png", ".jpg", ".jpeg"], + "linux": [".png", ".jpg", ".jpeg", ".bmp"], + "web": [".png", ".jpg", ".jpeg", ".svg"], + "apk": [".png", ".jpg", ".jpeg"], + "aab": [".png", ".jpg", ".jpeg"], + "ipa": [".png", ".jpg", ".jpeg"], + "ios-simulator": [".png", ".jpg", ".jpeg"], + } + def find_platform_image( self, src_path: Path, @@ -2272,7 +2286,13 @@ def find_platform_image( hash: HashStamp, ): """ - Find first matching image file by base name and queue it for copy. + Find the best matching image file for the current target platform. + + When multiple files share the same base name (e.g. ``icon.icns``, + ``icon.ico``, ``icon.png``), the method picks the one whose extension + is most appropriate for the build target. For example, ``.icns`` is + skipped on Windows builds because ``flutter_launcher_icons`` cannot + decode it. Args: src_path: Source assets directory. @@ -2282,20 +2302,35 @@ def find_platform_image( hash: Hash accumulator used for change detection. Returns: - File name of matched image, or `None` if not found. + File name of matched image, or ``None`` if not found. """ images = glob.glob(str(src_path.joinpath(f"{image_name}.*"))) - if len(images) > 0: - if self.verbose > 0: - console.log( - f'Found "{image_name}" image at {images[0]}', style=verbose1_style - ) - copy_ops.append((images[0], dest_path)) - hash.update(images[0]) - hash.update(Path(images[0]).stat().st_mtime) - return Path(images[0]).name - return None + if not images: + return None + + preferred = self._PLATFORM_IMAGE_PREFERENCE.get( + self.target_platform, [".png"] + ) + + def _sort_key(path: str) -> int: + ext = Path(path).suffix.lower() + try: + return preferred.index(ext) + except ValueError: + return len(preferred) + + images.sort(key=_sort_key) + + best = images[0] + if self.verbose > 0: + console.log( + f'Found "{image_name}" image at {best}', style=verbose1_style + ) + copy_ops.append((best, dest_path)) + hash.update(best) + hash.update(Path(best).stat().st_mtime) + return Path(best).name def run(self, args, cwd, env: Optional[dict] = None, capture_output=True): """ From b7b2facf86012f825bc54d4c74280c217007d7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E9=93=AD?= <60115106+HG-ha@users.noreply.github.com> Date: Mon, 6 Apr 2026 02:51:07 +0800 Subject: [PATCH 2/6] Fix formatting in docstring for image matching method --- .../flet-cli/src/flet_cli/commands/build_base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py index 8a0fa819de..eb331cb574 100644 --- a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py +++ b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py @@ -2288,10 +2288,10 @@ def find_platform_image( """ Find the best matching image file for the current target platform. - When multiple files share the same base name (e.g. ``icon.icns``, - ``icon.ico``, ``icon.png``), the method picks the one whose extension - is most appropriate for the build target. For example, ``.icns`` is - skipped on Windows builds because ``flutter_launcher_icons`` cannot + When multiple files share the same base name (e.g. icon.icns, + icon.ico, icon.png), the method picks the one whose extension + is most appropriate for the build target. For example, .icns is + skipped on Windows builds because flutter_launcher_icons cannot decode it. Args: @@ -2302,7 +2302,7 @@ def find_platform_image( hash: Hash accumulator used for change detection. Returns: - File name of matched image, or ``None`` if not found. + File name of matched image, or None if not found. """ images = glob.glob(str(src_path.joinpath(f"{image_name}.*"))) From 50507e392f77af8535cad22e17a34eea8a3a46e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E9=93=AD?= <60115106+HG-ha@users.noreply.github.com> Date: Mon, 6 Apr 2026 03:36:49 +0800 Subject: [PATCH 3/6] Update docstring formatting for clarity --- .../flet-cli/src/flet_cli/commands/build_base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py index eb331cb574..a67231d916 100644 --- a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py +++ b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py @@ -2288,10 +2288,10 @@ def find_platform_image( """ Find the best matching image file for the current target platform. - When multiple files share the same base name (e.g. icon.icns, - icon.ico, icon.png), the method picks the one whose extension - is most appropriate for the build target. For example, .icns is - skipped on Windows builds because flutter_launcher_icons cannot + When multiple files share the same base name (e.g. `icon.icns`, + `icon.ico`, `icon.png`), the method picks the one whose extension + is most appropriate for the build target. For example, `.icns` is + skipped on Windows builds because `flutter_launcher_icons` cannot decode it. Args: @@ -2302,7 +2302,7 @@ def find_platform_image( hash: Hash accumulator used for change detection. Returns: - File name of matched image, or None if not found. + File name of matched image, or `None` if not found. """ images = glob.glob(str(src_path.joinpath(f"{image_name}.*"))) From 624c017488543b832c043d3046503a003ce9c1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E9=93=AD?= <60115106+HG-ha@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:06:19 +0800 Subject: [PATCH 4/6] Refactor platform image preference and exclusion logic Refactor image preference handling by introducing excluded extensions for each platform. Update the image selection logic to filter out incompatible formats based on the target platform. --- .../src/flet_cli/commands/build_base.py | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py index a67231d916..c8ba785773 100644 --- a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py +++ b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py @@ -2263,18 +2263,18 @@ def rename_android_build_outputs(self): style=verbose1_style, ) - # Preferred icon extensions per target platform. Earlier entries win. - # .png is a universal fallback supported by flutter_launcher_icons on - # every platform, while .icns is macOS-only and .ico is Windows-only. - _PLATFORM_IMAGE_PREFERENCE: dict[str, list[str]] = { - "windows": [".ico", ".png", ".jpg", ".jpeg", ".bmp"], - "macos": [".icns", ".png", ".jpg", ".jpeg"], - "linux": [".png", ".jpg", ".jpeg", ".bmp"], - "web": [".png", ".jpg", ".jpeg", ".svg"], - "apk": [".png", ".jpg", ".jpeg"], - "aab": [".png", ".jpg", ".jpeg"], - "ipa": [".png", ".jpg", ".jpeg"], - "ios-simulator": [".png", ".jpg", ".jpeg"], + # Extensions incompatible with each target platform. + # .icns is macOS-only and .ico is Windows-only; other platforms + # exclude both so flutter_launcher_icons always gets a decodable format. + _PLATFORM_EXCLUDED_EXTENSIONS: dict[str, list[str]] = { + "windows": [".icns"], + "macos": [".ico"], + "linux": [".icns", ".ico"], + "web": [".icns", ".ico"], + "apk": [".icns", ".ico"], + "aab": [".icns", ".ico"], + "ipa": [".icns", ".ico"], + "ios-simulator": [".icns"], } def find_platform_image( @@ -2289,10 +2289,10 @@ def find_platform_image( Find the best matching image file for the current target platform. When multiple files share the same base name (e.g. `icon.icns`, - `icon.ico`, `icon.png`), the method picks the one whose extension - is most appropriate for the build target. For example, `.icns` is - skipped on Windows builds because `flutter_launcher_icons` cannot - decode it. + `icon.ico`, `icon.png`), the method filters out formats that are + incompatible with the build target before selecting the first match. + For example, `.icns` is skipped on Windows builds because + `flutter_launcher_icons` cannot decode it. Args: src_path: Source assets directory. @@ -2309,18 +2309,14 @@ def find_platform_image( if not images: return None - preferred = self._PLATFORM_IMAGE_PREFERENCE.get( - self.target_platform, [".png"] + excluded = self._PLATFORM_EXCLUDED_EXTENSIONS.get( + self.target_platform, [] ) + if excluded: + images = [p for p in images if Path(p).suffix.lower() not in excluded] - def _sort_key(path: str) -> int: - ext = Path(path).suffix.lower() - try: - return preferred.index(ext) - except ValueError: - return len(preferred) - - images.sort(key=_sort_key) + if not images: + return None best = images[0] if self.verbose > 0: From 46f6372ef5908d9962080a70a9d8700340becf92 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 8 Apr 2026 12:03:01 -0700 Subject: [PATCH 5/6] Inline platform-specific icon filtering Remove the _PLATFORM_EXCLUDED_EXTENSIONS class attribute and replace the exclusion lookup with an inline filter in find_platform_image. The new code directly excludes .icns on non-macOS targets and .ico on non-Windows targets (so flutter_launcher_icons receives a decodable file), simplifying the logic and reducing indirection without changing behavior. --- .../src/flet_cli/commands/build_base.py | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py index c8ba785773..29bff9fd2c 100644 --- a/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py +++ b/sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py @@ -2263,20 +2263,6 @@ def rename_android_build_outputs(self): style=verbose1_style, ) - # Extensions incompatible with each target platform. - # .icns is macOS-only and .ico is Windows-only; other platforms - # exclude both so flutter_launcher_icons always gets a decodable format. - _PLATFORM_EXCLUDED_EXTENSIONS: dict[str, list[str]] = { - "windows": [".icns"], - "macos": [".ico"], - "linux": [".icns", ".ico"], - "web": [".icns", ".ico"], - "apk": [".icns", ".ico"], - "aab": [".icns", ".ico"], - "ipa": [".icns", ".ico"], - "ios-simulator": [".icns"], - } - def find_platform_image( self, src_path: Path, @@ -2305,24 +2291,26 @@ def find_platform_image( File name of matched image, or `None` if not found. """ - images = glob.glob(str(src_path.joinpath(f"{image_name}.*"))) - if not images: - return None - - excluded = self._PLATFORM_EXCLUDED_EXTENSIONS.get( - self.target_platform, [] + # .icns is macOS-only and .ico is Windows-only; filter out + # incompatible formats so flutter_launcher_icons gets a decodable file. + images = list( + filter( + lambda p: not ( + (ext := Path(p).suffix.lower()) == ".icns" + and self.target_platform != "macos" + or ext == ".ico" + and self.target_platform != "windows" + ), + glob.glob(str(src_path.joinpath(f"{image_name}.*"))), + ) ) - if excluded: - images = [p for p in images if Path(p).suffix.lower() not in excluded] if not images: return None best = images[0] if self.verbose > 0: - console.log( - f'Found "{image_name}" image at {best}', style=verbose1_style - ) + console.log(f'Found "{image_name}" image at {best}', style=verbose1_style) copy_ops.append((best, dest_path)) hash.update(best) hash.update(Path(best).stat().st_mtime) From 100043e36d0c8c6725963bc6d6e844515ee86856 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Fri, 10 Apr 2026 11:15:36 -0700 Subject: [PATCH 6/6] docs: add CHANGELOG entry for find_platform_image fix Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 138a25c99a..20f664630e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Bug fixes * Fix `flet build` and `flet publish` dependency parsing for `project.dependencies` and Poetry constraints with `<`/`<=`, and add coverage for normalized requirement handling ([#6332](https://github.com/flet-dev/flet/issues/6332), [#6340](https://github.com/flet-dev/flet/pull/6340)) by @td3447. +* 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. ### Other changes