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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Bug fixes

* Fix `page.window.maximized = True` intermittently reverting to unmaximized right after startup on macOS, when set in the same patch as `page.title` (e.g. `page.title = "My App"; page.window.maximized = True` in `main()`) by @davidlawson.
* Fix `flet build` picking a non-decodable icon/splash image when several files share a base name, producing a machine-dependent `NoDecoderForImageFormatException` from `flutter_launcher_icons`. When an app's `assets` held, say, both `icon.png` and `icon.svg`, `find_platform_image` selected the first match from `glob.glob(...)` — whose order is filesystem-dependent — so the same app could pick `icon.png` on one machine and `icon.svg` on another (SVG is vector and can't be decoded by the raster icon/splash generators), turning a working build into a crash purely based on directory listing order. Candidates are now filtered to formats the generators can actually decode (`.svg` is dropped everywhere; `.icns` stays macOS-only and `.ico` Windows-only) and ranked so a raster image (`.png` first) always wins, making the choice deterministic across machines. When the only supplied image is an SVG (no raster sibling), it's skipped with a build-log warning and the default Flet icon is used instead of crashing by @FeodorFitsner.
* Fix modal controls (`AlertDialog`, `CupertinoAlertDialog`, `BottomSheet`, `CupertinoBottomSheet`) crashing to a black screen with "setState()/markNeedsBuild() called during build" when they close in the same frame that another route or overlay opens — e.g. dismissing a bottom sheet and showing a `SnackBar` from one handler. The close path popped the route synchronously during `build`, so the exit animation notified a listener that was mid-build. Each modal now tracks its own `ModalRoute` and closes it in a post-frame callback, popping *that* route (never the topmost one); `View`'s confirm-pop pops its own route too, so a modal dismissed in the same tick as a view pop can no longer dismiss the wrong one by @FeodorFitsner.

Expand Down
19 changes: 19 additions & 0 deletions packages/flet/lib/src/services/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import '../utils/window.dart';
class WindowService extends FletService with WindowListener {
final Completer<void> _initWindowStateCompleter = Completer<void>();
Future<void> _pendingWindowUpdate = Future.value();
bool _updateScheduled = false;
String? _title;
Color? _bgColor;
double? _width;
Expand Down Expand Up @@ -111,7 +112,25 @@ class WindowService extends FletService with WindowListener {
}

void _scheduleWindowUpdate() {
// Coalesce calls that land before the previously-scheduled run has
// actually started: both `update()` (the Window control's own patch)
// and `_onPageChanged()` (a title change on the *page*, a different
// control) call this method, and a single patch that changes both the
// window and the title fires both — `_updateWindow` re-reads every
// property live from `control`/`parent` when it runs, so a second run
// queued back-to-back before the first even starts would just re-apply
// the same already-current values. Without this, two runs execute in
// sequence for one patch, and if the first starts an animated native
// operation (e.g. macOS NSWindow.zoom for maximize/unmaximize), the
// second can reissue that same call while it's still animating —
// zoom: toggles, so a single `maximized = true` can silently animate
// back to unmaximized right after being applied.
if (_updateScheduled) {
return;
}
_updateScheduled = true;
_pendingWindowUpdate = _pendingWindowUpdate.catchError((_) {}).then((_) {
_updateScheduled = false;
if (_initWindowStateCompleter.isCompleted) {
return _updateWindow(control.backend);
}
Expand Down
Loading