Skip to content
Open
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
4 changes: 3 additions & 1 deletion client/linux/my_application.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ static void my_application_activate(GApplication* application) {
}

gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
// Realize the native window without showing it so Dart can decide when to
// display and position the window.
gtk_widget_realize(GTK_WIDGET(window));

g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
Expand Down
10 changes: 8 additions & 2 deletions client/windows/runner/flutter_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <optional>

#include "flutter/generated_plugin_registrant.h"
#include "utils.h"

FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
Expand All @@ -27,8 +28,13 @@ bool FlutterWindow::OnCreate() {
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());

flutter_controller_->engine()->SetNextFrameCallback([&]() {
this->Show();
const bool hide_window_on_start =
HasEnvironmentVariable(L"FLET_HIDE_WINDOW_ON_START");
flutter_controller_->engine()->SetNextFrameCallback([this,
hide_window_on_start]() {
if (!hide_window_on_start) {
Show();
}
});

// Flutter can complete the first frame before the "show window" callback is
Expand Down
6 changes: 6 additions & 0 deletions client/windows/runner/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ std::vector<std::string> GetCommandLineArguments() {
return command_line_arguments;
}

bool HasEnvironmentVariable(const wchar_t* name) {
::SetLastError(ERROR_SUCCESS);
const DWORD value_length = ::GetEnvironmentVariableW(name, nullptr, 0);
return value_length > 0 || ::GetLastError() != ERROR_ENVVAR_NOT_FOUND;
}

std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
Expand Down
4 changes: 4 additions & 0 deletions client/windows/runner/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string);
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();

// Returns true when the given environment variable exists, even if it is set
// to an empty value.
bool HasEnvironmentVariable(const wchar_t* name);

#endif // RUNNER_UTILS_H_
162 changes: 124 additions & 38 deletions packages/flet/lib/src/services/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:window_manager/window_manager.dart';

import '../flet_backend.dart';
import '../flet_service.dart';
import '../models/window_state.dart';
import '../utils/alignment.dart';
import '../utils/colors.dart';
import '../utils/desktop.dart';
Expand Down Expand Up @@ -51,6 +52,10 @@ class WindowService extends FletService with WindowListener {
bool? _skipTaskBar;
double? _progressBar;
bool? _ignoreMouseEvents;
Alignment? _deferredAlignmentOnShow;
bool _deferredCenterOnShow = false;
bool _isClosing = false;
bool _applyingDeferredPlacement = false;
bool _listenersAttached = false;

WindowService({required super.control});
Expand All @@ -68,22 +73,7 @@ class WindowService extends FletService with WindowListener {
Future<void> _initWindowState() async {
try {
final windowState = await getWindowState();
_width = windowState.width;
_height = windowState.height;
_top = windowState.top;
_left = windowState.left;
_opacity = windowState.opacity;
_minimizable = windowState.minimizable;
_maximizable = windowState.maximizable;
_fullScreen = windowState.fullScreen;
_resizable = windowState.resizable;
_alwaysOnTop = windowState.alwaysOnTop;
_preventClose = windowState.preventClose;
_minimized = windowState.minimized;
_maximized = windowState.maximized;
_visible = windowState.visible;
_focused = windowState.focused;
_skipTaskBar = windowState.skipTaskBar;
_cacheWindowState(windowState);

if (!_listenersAttached) {
windowManager.addListener(this);
Expand Down Expand Up @@ -130,6 +120,77 @@ class WindowService extends FletService with WindowListener {
});
}

bool _shouldDeferPlacementUntilShow() {
return isLinuxDesktop() && (_visible == null || _visible == false);
}

void _cacheWindowState(WindowState state) {
_width = state.width;
_height = state.height;
_top = state.top;
_left = state.left;
_opacity = state.opacity;
_minimizable = state.minimizable;
_maximizable = state.maximizable;
_fullScreen = state.fullScreen;
_resizable = state.resizable;
_alwaysOnTop = state.alwaysOnTop;
_preventClose = state.preventClose;
_minimized = state.minimized;
_maximized = state.maximized;
_visible = state.visible;
_focused = state.focused;
_skipTaskBar = state.skipTaskBar;
}

WindowState _snapshotWindowState({bool? visible, bool? focused}) {
return WindowState(
maximized: _maximized ?? false,
minimized: _minimized ?? false,
fullScreen: _fullScreen ?? false,
alwaysOnTop: _alwaysOnTop ?? false,
focused: focused ?? _focused ?? false,
visible: visible ?? _visible ?? false,
minimizable: _minimizable ?? true,
maximizable: _maximizable ?? true,
resizable: _resizable ?? true,
preventClose: _preventClose ?? false,
skipTaskBar: _skipTaskBar ?? false,
width: _width ?? 0,
height: _height ?? 0,
top: _top ?? 0,
left: _left ?? 0,
opacity: _opacity ?? 1,
);
}

Future<void> _applyDeferredPlacementAfterShow() async {
if (!isLinuxDesktop() ||
_applyingDeferredPlacement ||
(_deferredAlignmentOnShow == null && !_deferredCenterOnShow)) {
return;
}

_applyingDeferredPlacement = true;
final deferredAlignment = _deferredAlignmentOnShow;
final deferredCenter = _deferredCenterOnShow;
_deferredAlignmentOnShow = null;
_deferredCenterOnShow = false;

try {
if (deferredAlignment != null) {
await setWindowAlignment(deferredAlignment, false);
}
if (deferredCenter) {
await centerWindow();
}
} catch (e) {
debugPrint("Error applying deferred window placement: $e");
} finally {
_applyingDeferredPlacement = false;
}
}

Future<void> _updateWindow(FletBackend backend) async {
if (!isDesktopPlatform()) {
return;
Expand Down Expand Up @@ -264,7 +325,12 @@ class WindowService extends FletService with WindowListener {
}

if (alignment != null && alignment != _alignment) {
await setWindowAlignment(alignment);
if (_shouldDeferPlacementUntilShow()) {
_deferredAlignmentOnShow = alignment;
} else {
await setWindowAlignment(alignment);
_deferredAlignmentOnShow = null;
}
_alignment = alignment;
}

Expand Down Expand Up @@ -386,12 +452,18 @@ class WindowService extends FletService with WindowListener {
break;
case "center":
await _pendingWindowUpdate;
await centerWindow();
if (_shouldDeferPlacementUntilShow()) {
_deferredAlignmentOnShow = null;
_deferredCenterOnShow = true;
} else {
await centerWindow();
}
break;
case "close":
await closeWindow();
break;
case "destroy":
_isClosing = true;
await destroyWindow();
break;
case "start_dragging":
Expand Down Expand Up @@ -427,25 +499,39 @@ class WindowService extends FletService with WindowListener {
if (["resize", "resized", "move"].contains(eventName)) {
return;
}
getWindowState().then((state) {
_width = state.width;
_height = state.height;
_top = state.top;
_left = state.left;
_opacity = state.opacity;
_minimized = state.minimized;
_maximized = state.maximized;
_minimizable = state.minimizable;
_maximizable = state.maximizable;
_fullScreen = state.fullScreen;
_resizable = state.resizable;
_alwaysOnTop = state.alwaysOnTop;
_preventClose = state.preventClose;
_visible = state.visible;
_focused = state.focused;
_skipTaskBar = state.skipTaskBar;

control.backend.onWindowEvent(eventName, state);
});
if (eventName == "close") {
final preventClose = _preventClose ?? false;
_isClosing = !preventClose;
control.backend.onWindowEvent(
eventName,
_snapshotWindowState(focused: preventClose ? null : false));
return;
}
if (eventName == "hide") {
_visible = false;
_focused = false;
control.backend.onWindowEvent(
eventName, _snapshotWindowState(visible: false, focused: false));
return;
}
if (_isClosing) {
control.backend.onWindowEvent(eventName, _snapshotWindowState());
return;
}

() async {
try {
if (eventName == "show") {
_visible = true;
await _applyDeferredPlacementAfterShow();
}

final state = await getWindowState();
_cacheWindowState(state);
control.backend.onWindowEvent(eventName, state);
} catch (e) {
debugPrint("Error handling window event $eventName: $e");
}
}();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <optional>

#include "flutter/generated_plugin_registrant.h"
#include "utils.h"

FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
Expand All @@ -27,8 +28,13 @@ bool FlutterWindow::OnCreate() {
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());

flutter_controller_->engine()->SetNextFrameCallback([&]() {
this->Show();
const bool hide_window_on_start =
HasEnvironmentVariable(L"FLET_HIDE_WINDOW_ON_START");
flutter_controller_->engine()->SetNextFrameCallback([this,
hide_window_on_start]() {
if (!hide_window_on_start) {
Show();
}
});

// Flutter can complete the first frame before the "show window" callback is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ std::vector<std::string> GetCommandLineArguments() {
return command_line_arguments;
}

bool HasEnvironmentVariable(const wchar_t* name) {
::SetLastError(ERROR_SUCCESS);
const DWORD value_length = ::GetEnvironmentVariableW(name, nullptr, 0);
return value_length > 0 || ::GetLastError() != ERROR_ENVVAR_NOT_FOUND;
}

std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string);
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();

// Returns true when the given environment variable exists, even if it is set
// to an empty value.
bool HasEnvironmentVariable(const wchar_t* name);

#endif // RUNNER_UTILS_H_
Loading