From 438e5e8be9c209ce1404a98cf058867c03e6f7bc Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Fri, 17 Jul 2026 16:57:11 -0700 Subject: [PATCH 1/3] Prepare 0.86.2 release: fix stale code under `flet debug android` (serious_python 4.3.4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `flet debug android` kept running previously-unpacked, stale app code after a re-run. `flet debug` reinstalls the same-version APK on each iteration (`flutter run` does an update install that preserves app data), and serious_python's on-device extraction cache — keyed only on `versionName+versionCode` — never saw the version change, so it skipped re-unpacking the new `app.zip`. Bump the build template's serious_python pin to 4.3.4, which folds the APK's `lastUpdateTime` into the cache key so every (re)install re-extracts the current code. `flet build apk` was never affected. Fixes #6682 --- CHANGELOG.md | 6 ++++++ packages/flet/CHANGELOG.md | 4 ++++ packages/flet/pubspec.yaml | 2 +- .../templates/build/{{cookiecutter.out_dir}}/pubspec.yaml | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec1f39e280..28319a0b84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.86.2 + +### Bug fixes + +* Fix code edits not taking effect under `flet debug android`: after re-running the command, the app kept executing the previously-unpacked, stale code instead of your changes. `flet debug` rebuilds and reinstalls the same-version APK on each iteration (`flutter run` does an update install that preserves app data), and `serious_python`'s on-device extraction cache — keyed only on `versionName+versionCode` — never saw the version change, so it skipped re-unpacking the new `app.zip`. Bumps `serious_python` to 4.3.4, which folds the APK's `lastUpdateTime` into that cache key so every (re)install re-extracts the current code while ordinary relaunches still hit the cache. `flet build apk` was never affected ([#6682](https://github.com/flet-dev/flet/issues/6682)) by @FeodorFitsner. + ## 0.86.1 ### Improvements diff --git a/packages/flet/CHANGELOG.md b/packages/flet/CHANGELOG.md index 9c78d050fe..6dc09ddc51 100644 --- a/packages/flet/CHANGELOG.md +++ b/packages/flet/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.86.2 + +_No changes in the `flet` Dart package; version bumped for release coordination with the `serious_python` 4.3.4 bump that fixes stale code under `flet debug android` ([#6682](https://github.com/flet-dev/flet/issues/6682))._ + ## 0.86.1 _No changes in the `flet` Dart package; version bumped for release coordination with the web client's readable app-package download errors ([#6680](https://github.com/flet-dev/flet/pull/6680))._ diff --git a/packages/flet/pubspec.yaml b/packages/flet/pubspec.yaml index 156c0f8673..e2944c8bce 100644 --- a/packages/flet/pubspec.yaml +++ b/packages/flet/pubspec.yaml @@ -2,7 +2,7 @@ name: flet description: Write entire Flutter app in Python or add server-driven UI experience into existing Flutter app. homepage: https://flet.dev repository: https://github.com/flet-dev/flet/tree/main/packages/flet -version: 0.86.1 +version: 0.86.2 # Supported platforms platforms: diff --git a/sdk/python/templates/build/{{cookiecutter.out_dir}}/pubspec.yaml b/sdk/python/templates/build/{{cookiecutter.out_dir}}/pubspec.yaml index de38318fbf..62db1b15ac 100644 --- a/sdk/python/templates/build/{{cookiecutter.out_dir}}/pubspec.yaml +++ b/sdk/python/templates/build/{{cookiecutter.out_dir}}/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: flet: path: ../../../../../packages/flet - serious_python: 4.3.3 + serious_python: 4.3.4 # MsgPack codec used by the dart_bridge FletBackendChannel implementation # in lib/main.dart — matches the wire format flet's existing socket From 004d053b5f3112dc487fbca6032f16a7d65dec72 Mon Sep 17 00:00:00 2001 From: TheEthicalBoy <98978078+ndonkoHenri@users.noreply.github.com> Date: Mon, 20 Jul 2026 02:04:03 +0200 Subject: [PATCH 2/3] fix(`flet-video`): re-apply player settings after visibility toggle (#6694) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(video): re-apply player settings after visibility toggle (#6683) Video's media player lives on the Flutter State, but the 'last applied' sentinels (_volume, _pitch, _playback_rate, _shuffle_playlist, _playlist_mode, _subtitle_track) were stored on the persistent control model. Toggling `visible` off/on disposes and recreates the State and its Player (a fresh Player defaults to volume 100), but the model-side sentinels survived, so build()'s diff saw no change and skipped re-applying them — leaving volume (and the others) stuck at their defaults. Move these sentinels to State instance fields so they share the player's lifetime (mirroring flet-audio), and reset them in _setup() to cover the didUpdateWidget control-swap path. build() now re-applies every player property to a freshly created Player. _fullscreen intentionally stays on the control model: it is Video-widget UI state, not a Player property. * add .gitignore to flet-secure-storage * update pubspec.yaml * update changelogs * update * docs(video): clarify why _setup resets the player-setting sentinels Address Copilot PR review: the old comment implied the visible off/on path is what makes the _setup reset necessary, but that path recreates the whole State (fields already null). The reset is actually required for the didUpdateWidget control-swap path, where the same State is reused with a fresh player. No behavior change. * ci: collapse duplicate push + pull_request runs via branch-keyed concurrency When a branch has an open PR, a push fires both a 'push' and a 'pull_request' event. ci.yml's concurrency group keyed on github.event_name (and PR number vs ref), so the two events landed in different groups and the full Build & Publish matrix ran twice, in parallel, for every push. Key the group on the branch (github.event.pull_request.head.ref || github.ref_name) instead — as flet-build-test/flet-test/macos-integration-tests already do — so push + PR collapse into a single run. Tag/main pushes have no PR event and stay uniquely keyed by ref_name, so releases are unaffected. Also add the same branch-keyed concurrency to zizmor.yml, which had none and therefore also ran twice per push. --- .github/workflows/ci.yml | 2 +- .github/workflows/zizmor.yml | 4 ++ CHANGELOG.md | 1 + client/pubspec.lock | 22 +++---- client/pubspec.yaml | 7 --- .../flutter/flet_secure_storage/.gitignore | 34 +++++++++++ sdk/python/packages/flet-video/CHANGELOG.md | 6 ++ .../src/flutter/flet_video/lib/src/video.dart | 60 +++++++++++++------ 8 files changed, 99 insertions(+), 37 deletions(-) create mode 100644 sdk/python/packages/flet-secure-storage/src/flutter/flet_secure_storage/.gitignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f016b6e52..239a0347d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ on: workflow_dispatch: concurrency: - group: ci-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} + group: ci-${{ github.workflow }}-${{ github.event.pull_request.head.ref || github.ref_name }} cancel-in-progress: true env: diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index e9ba32c042..1651ebb451 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -4,6 +4,10 @@ on: push: pull_request: +concurrency: + group: zizmor-${{ github.workflow }}-${{ github.event.pull_request.head.ref || github.ref_name }} + cancel-in-progress: true + permissions: {} jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 28319a0b84..bea527d825 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Bug fixes * Fix code edits not taking effect under `flet debug android`: after re-running the command, the app kept executing the previously-unpacked, stale code instead of your changes. `flet debug` rebuilds and reinstalls the same-version APK on each iteration (`flutter run` does an update install that preserves app data), and `serious_python`'s on-device extraction cache — keyed only on `versionName+versionCode` — never saw the version change, so it skipped re-unpacking the new `app.zip`. Bumps `serious_python` to 4.3.4, which folds the APK's `lastUpdateTime` into that cache key so every (re)install re-extracts the current code while ordinary relaunches still hit the cache. `flet build apk` was never affected ([#6682](https://github.com/flet-dev/flet/issues/6682)) by @FeodorFitsner. +* Fix `flet_video.Video` resetting its `volume` (and `pitch`, `playback_rate`, `shuffle_playlist`, `playlist_mode`, `subtitle_track`) to the player's defaults after toggling `visible` off then on — e.g. `volume` jumped back to `100`. Hiding a `Video` disposes its native `media_kit` player and showing it recreates a fresh one at default settings; the "last-applied" tracking now lives with the player (not the persistent control model) and is reset on recreation, so `build()` re-applies every setting to the new player ([#6683](https://github.com/flet-dev/flet/issues/6683), [#6694](https://github.com/flet-dev/flet/pull/6694)) by @ndonkoHenri. ## 0.86.1 diff --git a/client/pubspec.lock b/client/pubspec.lock index 0558090288..ee5bab4d12 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -213,10 +213,10 @@ packages: dependency: transitive description: name: connectivity_plus - sha256: "62ffa266d9a23b79fb3fcbc206afc00bb979417ba57b1324c546b5aab95ba057" + sha256: "25cebb79dfe304022550e0c3c893ae051ded07183d2607f7b88473cb3ade33cb" url: "https://pub.dev" source: hosted - version: "7.1.1" + version: "7.3.0" connectivity_plus_platform_interface: dependency: transitive description: @@ -285,10 +285,10 @@ packages: dependency: transitive description: name: device_info_plus - sha256: b4fed1b2835da9d670d7bed7db79ae2a94b0f5ad6312268158a9b5479abbacdd + sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c" url: "https://pub.dev" source: hosted - version: "12.4.0" + version: "12.3.0" device_info_plus_platform_interface: dependency: transitive description: @@ -359,7 +359,7 @@ packages: path: "../packages/flet" relative: true source: path - version: "0.86.0" + version: "0.86.2" flet_ads: dependency: "direct main" description: @@ -1370,13 +1370,13 @@ packages: source: hosted version: "2.0.4" screen_brightness: - dependency: "direct overridden" + dependency: transitive description: name: screen_brightness - sha256: "5f70754028f169f059fdc61112a19dcbee152f8b293c42c848317854d650cba3" + sha256: e0edf92c08889e8f493cde291e7c687db2b4a1471f2371c074070b75d7c7d79b url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.11" screen_brightness_android: dependency: transitive description: @@ -1394,13 +1394,13 @@ packages: source: hosted version: "2.1.4" screen_brightness_macos: - dependency: "direct overridden" + dependency: transitive description: name: screen_brightness_macos - sha256: "278712cf5288db57bd335968cbfb2ec5441028f1ee2fcbdc8d1582d8210a3442" + sha256: b0957237b842d846a84363b69f229b339a8f91faced55041e245b2ebff7b0142 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" screen_brightness_ohos: dependency: transitive description: diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 0ba147dc8d..3e4adc8a13 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -99,13 +99,6 @@ dependency_overrides: path: ../packages/flet screen_retriever: 0.2.1 # this one migrated to SPM, but 0.2.0 is used by some other packages - screen_brightness: 2.1.7 - # screen_brightness_macos 2.1.3 ("Fix: swift package manager warning") ships a - # Package.swift declaring macOS 10.11, below FlutterFramework's 10.15 floor, - # which breaks Swift Package Manager resolution. Pin the last good impl until - # upstream fixes it: https://github.com/aaassseee/screen_brightness/issues/99 - screen_brightness_macos: 2.1.2 - dev_dependencies: flutter_test: sdk: flutter diff --git a/sdk/python/packages/flet-secure-storage/src/flutter/flet_secure_storage/.gitignore b/sdk/python/packages/flet-secure-storage/src/flutter/flet_secure_storage/.gitignore new file mode 100644 index 0000000000..ed7794f2ab --- /dev/null +++ b/sdk/python/packages/flet-secure-storage/src/flutter/flet_secure_storage/.gitignore @@ -0,0 +1,34 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ +.flutter-plugins +.flutter-plugins-dependencies + +# override parent rules +!lib/ diff --git a/sdk/python/packages/flet-video/CHANGELOG.md b/sdk/python/packages/flet-video/CHANGELOG.md index 63d23a9fb5..308f33bf6b 100644 --- a/sdk/python/packages/flet-video/CHANGELOG.md +++ b/sdk/python/packages/flet-video/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## 0.86.2 + +### Fixed + +- `Video.volume` (and `pitch`, `playback_rate`, `shuffle_playlist`, `playlist_mode`, `subtitle_track`) resetting to the player's defaults after toggling `visible` off then on. Showing a hidden `Video` recreates its native player at default settings; the "last-applied" tracking now lives with the player and resets on recreation, so these settings are re-applied ([#6683](https://github.com/flet-dev/flet/issues/6683), [#6694](https://github.com/flet-dev/flet/pull/6694)) by @ndonkoHenri. + ## 0.85.0 ### Added diff --git a/sdk/python/packages/flet-video/src/flutter/flet_video/lib/src/video.dart b/sdk/python/packages/flet-video/src/flutter/flet_video/lib/src/video.dart index 03eceb7c45..69f3e80319 100644 --- a/sdk/python/packages/flet-video/src/flutter/flet_video/lib/src/video.dart +++ b/sdk/python/packages/flet-video/src/flutter/flet_video/lib/src/video.dart @@ -29,6 +29,24 @@ class _VideoControlState extends State with FletStoreMixin { bool _initialized = false; Future? _openFuture; + /// Last values applied to the current [_player]. They share the player's + /// lifetime (both are (re)created in [_setup]), so [build] diffs against them + /// to avoid re-applying unchanged properties. + /// + /// [_setup] resets them to `null` whenever it creates a new player so [build] + /// re-applies every property to it. This is required on the `didUpdateWidget` + /// control-swap path, where the same State instance is reused with a fresh + /// player; on the `visible` off/on path the whole State (and these fields) is + /// recreated anyway, so they already start as `null`. + /// + /// Keep this list in lockstep with the property diffs in [build]. + double? _volume; + double? _pitch; + double? _playbackRate; + bool? _shufflePlaylist; + PlaylistMode? _playlistMode; + SubtitleTrack? _subtitleTrack; + /// Snapshot of the last-known playlist, used to diff against incoming /// updates so single add/remove changes can be applied without a full reload. dynamic _playlist; @@ -152,6 +170,15 @@ class _VideoControlState extends State with FletStoreMixin { _player = Player(configuration: playerConfig); + // The new player starts with default settings, so forget what was applied + // to the previous one and let build() re-apply everything. + _volume = null; + _pitch = null; + _playbackRate = null; + _shufflePlaylist = null; + _playlistMode = null; + _subtitleTrack = null; + final videoControllerConfiguration = parseControllerConfiguration( control.get("configuration"), const VideoControllerConfiguration())!; _controller = @@ -344,14 +371,15 @@ class _VideoControlState extends State with FletStoreMixin { parsePlaylistMode(widget.control.getString("playlist_mode")); var fullscreen = widget.control.getBool("fullscreen", false)!; - // previous values - final prevVolume = widget.control.getDouble("_volume"); - final prevPitch = widget.control.getDouble("_pitch"); - final prevPlaybackRate = widget.control.getDouble("_playback_rate"); - final prevShufflePlaylist = widget.control.getBool("_shuffle_playlist"); - final PlaylistMode? prevPlaylistMode = widget.control.get("_playlist_mode"); - final SubtitleTrack? prevSubtitleTrack = - widget.control.get("_subtitle_track"); + // previous values (tracked per-player; see field docs) + final prevVolume = _volume; + final prevPitch = _pitch; + final prevPlaybackRate = _playbackRate; + final prevShufflePlaylist = _shufflePlaylist; + final PlaylistMode? prevPlaylistMode = _playlistMode; + final SubtitleTrack? prevSubtitleTrack = _subtitleTrack; + // Fullscreen is Video-widget UI state (not a player property), so it stays + // on the control model and is intentionally not reset with the player. final prevFullscreen = widget.control.getBool("_fullscreen", false)!; Video video = Video( @@ -382,20 +410,19 @@ class _VideoControlState extends State with FletStoreMixin { volume != prevVolume && volume >= 0 && volume <= 100) { - widget.control.updateProperties({"_volume": volume}, python: false); + _volume = volume; await _player.setVolume(volume); } // pitch if (pitch != null && pitch != prevPitch) { - widget.control.updateProperties({"_pitch": pitch}, python: false); + _pitch = pitch; await _player.setPitch(pitch); } // playbackRate if (playbackRate != null && playbackRate != prevPlaybackRate) { - widget.control - .updateProperties({"_playback_rate": playbackRate}, python: false); + _playbackRate = playbackRate; await _player.setRate(playbackRate); } @@ -406,15 +433,13 @@ class _VideoControlState extends State with FletStoreMixin { // shufflePlaylist if (shufflePlaylist != null && shufflePlaylist != prevShufflePlaylist) { - widget.control.updateProperties({"_shuffle_playlist": shufflePlaylist}, - python: false); + _shufflePlaylist = shufflePlaylist; await _player.setShuffle(shufflePlaylist); } // playlistMode if (playlistMode != null && playlistMode != prevPlaylistMode) { - widget.control - .updateProperties({"_playlist_mode": playlistMode}, python: false); + _playlistMode = playlistMode; await _player.setPlaylistMode(playlistMode); } @@ -422,8 +447,7 @@ class _VideoControlState extends State with FletStoreMixin { if (subtitleTrack != null && subtitleTrack != prevSubtitleTrack) { await _openFuture; if (!_initialized) return; - widget.control.updateProperties({"_subtitle_track": subtitleTrack}, - python: false); + _subtitleTrack = subtitleTrack; await _player.setSubtitleTrack(subtitleTrack); } From b3e4c942633d7fe157cc9a4ff31eb1fd86b439f3 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 19 Jul 2026 20:35:01 -0700 Subject: [PATCH 3/3] Bump python-build release date to 20260719 Updates the pinned `PYTHON_BUILD_RELEASE_DATE` in `python_versions.py` so Flet CLI tracks the latest python-build release date and stays aligned with serious_python version metadata. --- .../packages/flet-cli/src/flet_cli/utils/python_versions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/packages/flet-cli/src/flet_cli/utils/python_versions.py b/sdk/python/packages/flet-cli/src/flet_cli/utils/python_versions.py index 5bcfefb5dd..348aee08a5 100644 --- a/sdk/python/packages/flet-cli/src/flet_cli/utils/python_versions.py +++ b/sdk/python/packages/flet-cli/src/flet_cli/utils/python_versions.py @@ -28,7 +28,7 @@ # python-build release this flet pins. Keep in sync with serious_python's # `pythonReleaseDate` (lib/src/python_versions.dart) — both should track the # same python-build release. -PYTHON_BUILD_RELEASE_DATE = "20260714" +PYTHON_BUILD_RELEASE_DATE = "20260719" RELEASE_DATE_ENV = "FLET_PYTHON_BUILD_RELEASE_DATE" MANIFEST_PATH_ENV = "FLET_PYTHON_BUILD_MANIFEST"