Skip to content

Commit 7e91fc5

Browse files
Fix app start with a hidden window (#5838)
* Refactor window control to fix app start with a hidden window Moved window logic from controls/window.dart to services/window.dart, converting WindowControl to WindowService and updating its integration. Updated page and core extension to use the new service, removed window overlay from view, and adjusted backend and related code to support the refactor. This change improves separation of concerns by treating window management as a service rather than a UI control. * Add usage instructions and startup message to example Added comments explaining how to run the example with the --hidden option using `flet run`. Also added a print statement to notify that the window is hidden on start and will show after 3 seconds. * Refactor time picker tests and add window resize example Refactored material time picker integration tests for improved clarity and consistency, updating dialog creation and page update order. Added a new example for window resizing in desktop apps. Updated golden image and other related test files. * Fix test flakiness in test_basic by updating page state Added explicit page update and await pump_and_settle before opening the time picker in test_basic. This ensures the page is fully rendered and stable before interaction, reducing test flakiness. * Remove rethrow in backend connection error handler Eliminated the rethrow statement in the FletBackend connection error handler to prevent exceptions from propagating and to handle errors more gracefully.
1 parent 725f1af commit 7e91fc5

File tree

13 files changed

+253
-203
lines changed

13 files changed

+253
-203
lines changed

packages/flet/lib/src/controls/page.dart

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import '../models/page_design.dart';
2020
import '../routing/route_parser.dart';
2121
import '../routing/route_state.dart';
2222
import '../routing/router_delegate.dart';
23+
import '../services/service_binding.dart';
2324
import '../services/service_registry.dart';
2425
import '../utils/device_info.dart';
2526
import '../utils/locale.dart';
@@ -57,6 +58,9 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
5758
late final AppLifecycleListener _appLifecycleListener;
5859
ServiceRegistry? _pageServices;
5960
ServiceRegistry? _userServices;
61+
String? _userServicesUid;
62+
ServiceBinding? _windowService;
63+
Control? _windowControl;
6064
bool? _prevOnKeyboardEvent;
6165
bool _keyboardHandlerSubscribed = false;
6266
String? _prevViewRoutes;
@@ -95,12 +99,15 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
9599

96100
_attachKeyboardListenerIfNeeded();
97101
widget.control.addInvokeMethodListener(_invokeMethod);
102+
widget.control.addListener(_onPageControlChanged);
103+
_ensureServiceRegistries();
98104
}
99105

100106
@override
101107
void didChangeDependencies() {
102108
debugPrint("Page.didChangeDependencies: ${widget.control.id}");
103109
super.didChangeDependencies();
110+
_ensureServiceRegistries();
104111
_loadFontsIfNeeded(FletBackend.of(context));
105112
}
106113

@@ -109,26 +116,7 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
109116
debugPrint("Page.didUpdateWidget: ${widget.control.id}");
110117
super.didUpdateWidget(oldWidget);
111118
_updateMultiViews();
112-
113-
// page services
114-
_pageServices ??= ServiceRegistry(
115-
control: widget.control,
116-
propertyName: "_services",
117-
backend: FletBackend.of(context));
118-
119-
// user services
120-
var userServicesControl = widget.control.child("_user_services");
121-
if (userServicesControl != null) {
122-
if (_userServices == null ||
123-
_userServices?.control.internals?["uid"] !=
124-
userServicesControl.internals?["uid"]) {
125-
_userServices = ServiceRegistry(
126-
control: userServicesControl,
127-
propertyName: "_services",
128-
backend: FletBackend.of(context));
129-
}
130-
}
131-
119+
_ensureServiceRegistries();
132120
_attachKeyboardListenerIfNeeded();
133121
_loadFontsIfNeeded(FletBackend.of(context));
134122
}
@@ -148,9 +136,60 @@ class _PageControlState extends State<PageControl> with WidgetsBindingObserver {
148136
HardwareKeyboard.instance.removeHandler(_handleKeyDown);
149137
}
150138
widget.control.removeInvokeMethodListener(_invokeMethod);
139+
widget.control.removeListener(_onPageControlChanged);
140+
_pageServices?.dispose();
141+
_userServices?.dispose();
142+
_windowService?.dispose();
151143
super.dispose();
152144
}
153145

146+
void _onPageControlChanged() {
147+
_ensureServiceRegistries();
148+
}
149+
150+
void _ensureServiceRegistries() {
151+
if (!mounted) {
152+
return;
153+
}
154+
var backend = FletBackend.of(context);
155+
156+
_pageServices ??= ServiceRegistry(
157+
control: widget.control,
158+
propertyName: "_services",
159+
backend: backend);
160+
161+
var userServicesControl = widget.control.child("_user_services");
162+
if (userServicesControl != null) {
163+
var uid = userServicesControl.internals?["uid"];
164+
if (_userServices == null || _userServicesUid != uid) {
165+
_userServices?.dispose();
166+
_userServices = ServiceRegistry(
167+
control: userServicesControl,
168+
propertyName: "_services",
169+
backend: backend);
170+
_userServicesUid = uid;
171+
}
172+
} else if (_userServices != null) {
173+
_userServices?.dispose();
174+
_userServices = null;
175+
_userServicesUid = null;
176+
}
177+
178+
var windowControl = widget.control.child("window", visibleOnly: false);
179+
if (windowControl != null) {
180+
if (!identical(windowControl, _windowControl)) {
181+
_windowService?.dispose();
182+
_windowService =
183+
ServiceBinding(control: windowControl, backend: backend);
184+
_windowControl = windowControl;
185+
}
186+
} else if (_windowService != null) {
187+
_windowService?.dispose();
188+
_windowService = null;
189+
_windowControl = null;
190+
}
191+
}
192+
154193
Future<dynamic> _invokeMethod(String name, dynamic args) async {
155194
debugPrint("Page.$name($args)");
156195

packages/flet/lib/src/controls/view.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,6 @@ class _ViewControlState extends State<ViewControl> {
168168
overlayWidgets.add(PageMedia(view: widget.control.parent));
169169
}
170170

171-
var windowControl = control.parent?.get("window");
172-
if (windowControl != null && isRootView && isDesktopPlatform()) {
173-
overlayWidgets.add(ControlWidget(control: windowControl));
174-
}
175171
}
176172

177173
Widget body = Stack(children: [

packages/flet/lib/src/flet_backend.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ class FletBackend extends ChangeNotifier {
192192
}
193193

194194
_registerClient() {
195+
debugPrint("Registering web client: $page");
195196
_send(
196197
Message(
197198
action: MessageAction.registerClient,
@@ -211,7 +212,7 @@ class FletBackend extends ChangeNotifier {
211212
"width": page.get("width"),
212213
"height": page.get("height"),
213214
"platform": page.get("platform"),
214-
"window": page.child("window")!.toMap(),
215+
"window": page.child("window", visibleOnly: false)!.toMap(),
215216
"media": page.get("media"),
216217
}).toMap()),
217218
unbuffered: true);
@@ -333,7 +334,7 @@ class FletBackend extends ChangeNotifier {
333334
if (isDesktopPlatform()) {
334335
var windowState = await getWindowState();
335336
debugPrint("Window state updated: $windowState");
336-
var window = page.child("window")!;
337+
var window = page.child("window", visibleOnly: false)!;
337338
updateControl(window.id, windowState.toMap());
338339
triggerControlEvent(window, "event", {"type": "resized"});
339340
}
@@ -470,8 +471,7 @@ class FletBackend extends ChangeNotifier {
470471
var template = appErrorMessage ?? defaultAppErrorMessageTemplate;
471472
final lines = const LineSplitter().convert(rawError);
472473
final message = lines.isNotEmpty ? lines.first : "";
473-
final details =
474-
lines.length > 1 ? lines.sublist(1).join("\n") : "";
474+
final details = lines.length > 1 ? lines.sublist(1).join("\n") : "";
475475
template = template.replaceAll("{message}", message);
476476
if (details.isEmpty) {
477477
template = template.replaceAll(RegExp(r'(\r?\n)*\{details\}'), "");

packages/flet/lib/src/flet_core_extension.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ import 'controls/time_picker.dart';
102102
import 'controls/transparent_pointer.dart';
103103
import 'controls/vertical_divider.dart';
104104
import 'controls/view.dart';
105-
import 'controls/window.dart';
106105
import 'controls/window_drag_area.dart';
107106
import 'flet_extension.dart';
108107
import 'flet_service.dart';
@@ -117,6 +116,7 @@ import 'services/shared_preferences.dart';
117116
import 'services/storage_paths.dart';
118117
import 'services/tester.dart';
119118
import 'services/url_launcher.dart';
119+
import 'services/window.dart';
120120
import 'utils/cupertino_icons.dart';
121121
import 'utils/material_icons.dart';
122122

@@ -358,8 +358,6 @@ class FletCoreExtension extends FletExtension {
358358
return VerticalDividerControl(key: key, control: control);
359359
case "View":
360360
return ViewControl(key: key, control: control);
361-
case "Window":
362-
return WindowControl(key: key, control: control);
363361
case "WindowDragArea":
364362
return WindowDragAreaControl(key: key, control: control);
365363
default:
@@ -386,6 +384,8 @@ class FletCoreExtension extends FletExtension {
386384
return SemanticsServiceControl(control: control);
387385
case "StoragePaths":
388386
return StoragePaths(control: control);
387+
case "Window":
388+
return WindowService(control: control);
389389
case "Tester":
390390
return TesterService(control: control);
391391
case "UrlLauncher":

0 commit comments

Comments
 (0)