前置条件
- MacBook Pro M2 macOS Tahoe (26.0) / Windows 11 24H2
- Flutter 3.35.3
功能需求
以目前的情况来看,Flutter 的桌面端软件其实跟移动端软件很像,因为 Flutter 桌面端目前默认只有一个窗口,而不是像原生的桌面端软件一样,经常会有很多个窗口。 比较不一样的是,移动端是直接全屏显示,桌面端的软件是以窗口的形式存在的。
而桌面端窗口大小可以设置,还可以由用户自由放大缩小。
所以一般桌面端软件都需要对窗口做一些配置,比如设置启动时的默认窗口大小以达到最佳的界面显示,设置最小的窗口大小来避免界面变形严重或者遮盖了重要界面等等。
我们希望应用启动后就占据整个屏幕,但是不是最大化,顶部状态栏,底部不要进行覆盖,类似 Chrome 浏览器的行为。
解决方法
可以使用 window_manager 插件来实现,但是发现达不到想要的效果。
后面发现 learnFlutter 还有一个开源库是 screen_retriever ,这个插件允许 Flutter 桌面 应用检索关于屏幕大小,显示,光标位置等信息。
试了下确实可以获取到屏幕的高度,可行的实现方式是使用 screen_retriever 获取到屏幕的高度之后再使用window_manager 设置窗口大小。
但是,上述的代码由于需要 Flutter 引擎初始化之后才能执行,因此会短暂先出现小窗口,然后窗口才会变大。整个过程中有明显的延迟,用户体验不好。
因此,还是需要从底层API层面进行窗口大小的调整,应用启动后,窗口大小立即调整完成,用户体验较好。
macOS 解决方案
方案一:
通过调用 NSWindow 的 zoom 函数,实现窗口的放大。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import Cocoa import FlutterMacOS @main class AppDelegate: FlutterAppDelegate { override func applicationDidFinishLaunching(_ notification: Notification) { mainFlutterWindow?.zoom(nil) } override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } } |
方案二:
通过调整 NSWindow 的 visibleFrame ,实现窗口的放大。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import Cocoa import FlutterMacOS @main class AppDelegate: FlutterAppDelegate { override func applicationDidFinishLaunching(_ notification: Notification) { // Get the main screen's visible frame if let screenFrame = NSScreen.main?.visibleFrame { // Set the window's frame to match the screen's visible frame mainFlutterWindow?.setFrame(screenFrame, display: true) } } override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } } |
方案三(推荐):
调整 MainFlutterWindow 的代码,直接设置窗口大小:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import Cocoa import FlutterMacOS class MainFlutterWindow: NSWindow { override func awakeFromNib() { let flutterViewController = FlutterViewController.init() // let windowFrame = self.frame self.contentViewController = flutterViewController // self.setFrame(windowFrame, display: true) // 设置窗口启动后铺满整个屏幕 // Get the main screen's visible frame if let screenFrame = NSScreen.main?.visibleFrame { // Set the window's frame to match the screen's visible frame self.setFrame(screenFrame, display: true) } else { let windowFrame = self.frame self.setFrame(windowFrame, display: true) } RegisterGeneratedPlugins(registry: flutterViewController) super.awakeFromNib() } } |
Windows 解决方案
Windows 系统下的解决方案比较简单,直接修改 ShowWindow 函数的参数,设置为 SW_MAXIMIZE 即可。代码如下:
1 2 3 4 5 6 7 |
..................................................... bool Win32Window::Show() { return ShowWindow(window_handle_, /*SW_SHOWNORMAL*/ SW_MAXIMIZE); } ..................................................... |
Linux 解决方案
Linux 系统下的使用的是 GTK,因此可以通过在初始化窗口之前调用 gtk_window_maximize 函数即可(此函数支持窗口显示之前调用)。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Implements GApplication::activate. static void my_application_activate(GApplication *application) { ................................................................ const int64_t window_width = 1280; const int64_t window_height = 720; gtk_window_set_default_size(window, window_width, window_height); // 设置窗口最大化 gtk_window_maximize(window); gtk_widget_show(GTK_WIDGET(window)); ................................................................ } |