Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Allow `[tool.flet.android.permission]` values to be TOML inline tables in addition to booleans — each `key = "value"` entry adds an `android:<key>="<value>"` attribute to the generated `<uses-permission>` element, unlocking modifiers like `android:maxSdkVersion` and `android:usesPermissionFlags` that real-world Android permissions (e.g. Bluetooth LE) require. The boolean form and the `--android-permissions` CLI flag are unchanged; a non-empty inline table is always emitted, an empty table (`{}`) is treated as `false`, and invalid value types fail the build with a clear error ([#6550](https://github.com/flet-dev/flet/issues/6550), [#6551](https://github.com/flet-dev/flet/pull/6551)) by @FeodorFitsner.
* Upgrade the bundled Pyodide runtime in the `flet build web` template from `0.27.5` to `0.27.7` (includes `micropip` `0.9.0`) ([#6549](https://github.com/flet-dev/flet/pull/6549)) by @FeodorFitsner.
* Drop generated `web/canvaskit/` build artifacts (`canvaskit.js`/`.wasm`/`.symbols` and the `chromium/` and `skwasm`/`skwasm_st` variants) from the `flet build web` template — these are produced by the Flutter web build and should not have been committed into the cookiecutter template ([#6549](https://github.com/flet-dev/flet/pull/6549)) by @FeodorFitsner.
* Cache the downloaded `flet-build-template.zip` across builds. The build template is bound to an exact Flet version and is immutable, so on every `flet build` / `flet debug` after the first, the CLI now uses a previously-downloaded zip from `$FLET_CACHE_DIR/build-template/v<flet-version>/` (defaulting to `~/.flet/cache/build-template/v<flet-version>/`) instead of re-fetching it via cookiecutter. The CLI also exports `FLET_CACHE_DIR` into the child Gradle process, so `serious_python_android` >= 1.0.1 lands its Python dist tarballs (`python-android-dart-<py>-<abi>.tar.gz`) in the same cache root by default — fixing the multi-minute "Creating app shell" / `downloadDistArchive_*` delay on every Android debug build. Custom `--template` URLs and the local-dev template path are unchanged ([#6555](https://github.com/flet-dev/flet/discussions/6555), [#6558](https://github.com/flet-dev/flet/pull/6558)) by @FeodorFitsner.
* Bump the bundled build template's `serious_python` dependency from `1.0.0` to `1.0.1` so Android builds pick up the new persistent Python-tarball cache + conditional-GET revalidation introduced in [`serious_python` 1.0.1](https://github.com/flet-dev/serious-python/blob/main/src/serious_python_android/CHANGELOG.md) ([#6558](https://github.com/flet-dev/flet/pull/6558)) by @FeodorFitsner.

### Bug fixes

Expand Down
15 changes: 12 additions & 3 deletions sdk/python/packages/flet-cli/src/flet_cli/commands/build_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,9 @@ def create_flutter_project(self, second_pass=False):
template_ref = flet.version.flet_version

is_local_dev = False
# Identity printed in status / hashed for invalidation; may differ from
# the path cookiecutter actually reads when caching kicks in below.
template_source = template_url
if template_url:
# User-provided template (git repo or local path) — use checkout
checkout = template_ref
Expand All @@ -1182,13 +1185,19 @@ def create_flutter_project(self, second_pass=False):
local_tpl = Path(__file__).resolve().parents[5] / "templates" / "build"
if local_tpl.is_dir():
template_url = str(local_tpl)
template_source = template_url
checkout = None
is_local_dev = True
else:
template_url = DEFAULT_TEMPLATE_URL.format(version=template_ref)
from flet_cli.utils.template_cache import get_cached_template_zip

template_source = DEFAULT_TEMPLATE_URL.format(version=template_ref)
template_url = str(
get_cached_template_zip(template_source, template_ref)
)
checkout = None

hash.update(template_url)
hash.update(template_source)
hash.update(template_ref)

template_dir = self.options.template_dir or self.get_pyproject(
Expand All @@ -1214,7 +1223,7 @@ def create_flutter_project(self, second_pass=False):
# create a new Flutter bootstrap project directory, if non-existent
if not second_pass:
self.flutter_dir.mkdir(parents=True, exist_ok=True)
status = f"[bold blue]Creating app shell from {template_url}"
status = f"[bold blue]Creating app shell from {template_source}"
if checkout:
status += f' with ref "{template_ref}"'
status += "..."
Expand Down
48 changes: 48 additions & 0 deletions sdk/python/packages/flet-cli/src/flet_cli/utils/template_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import os
import shutil
import urllib.request
from pathlib import Path


def get_cache_root() -> Path:
"""Resolve the Flet on-disk cache root.

Uses `$FLET_CACHE_DIR` if set, otherwise `~/.flet/cache`. The resolved
path is exported back into the process environment so child processes
(notably the Gradle build of `serious_python_android`) share the same
cache root by default.
"""
root = os.environ.get("FLET_CACHE_DIR")
cache_root = Path(root).expanduser() if root else Path.home() / ".flet" / "cache"
os.environ["FLET_CACHE_DIR"] = str(cache_root)
return cache_root


def get_cached_template_zip(url: str, version: str) -> Path:
"""Return a local path to `flet-build-template.zip` for `version`.

The build template at a versioned release URL is immutable, so caching
is a simple "use if present, else download once" — no revalidation.
"""
cache_path = (
get_cache_root() / "build-template" / f"v{version}" / "flet-build-template.zip"
)

if cache_path.exists() and cache_path.stat().st_size > 0:
return cache_path

cache_path.parent.mkdir(parents=True, exist_ok=True)
tmp_path = cache_path.with_suffix(cache_path.suffix + ".tmp")

try:
with urllib.request.urlopen(url) as resp, open(tmp_path, "wb") as out:
shutil.copyfileobj(resp, out)
out.flush()
os.fsync(out.fileno())
os.replace(tmp_path, cache_path)
except BaseException:
if tmp_path.exists():
tmp_path.unlink(missing_ok=True)
raise

return cache_path
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dependencies:

flet:
path: ../../../../../packages/flet
serious_python: 1.0.0
serious_python: 1.0.1

package_info_plus: ^9.0.0
path_provider: ^2.1.4
Expand Down
Loading