Flutter桌面端窗口控制——window_manager插件的使用与跨平台问题

Anrola Lv1

Flutter有以其出色的跨平台能力,但是在window平台特有的窗口控制、标题栏自定义、系统交互等需求,却需要额外的库来支持。如果需要更沉浸、更个性化的体验,熟悉window_manager的使用就很重要了。

1.核心功能

使用window_manager你将可以做到:

  • 移除系统标题栏。
  • 绘制一套风格独特的标题栏和控制按钮,与整体UI设计语言保持一致。
  • 控制窗口的初始位置、大小、最小/最大尺寸限制。
  • 实现一些高级交互,如窗口置顶、监听窗口状态变化(获得焦点、失去焦点、最大化、最小化等)。
  • 定义非标准区域(比如应用内的某个工具栏)也能拖动窗口。

2.安装

在你的flutter项目的pubspec.yaml文件里添加依赖。

1
2
dependencies: 
window_manager: ^0.5.2

这里版本号不一定是最新的,可以到这里查看最新版本。
当然你也可以写window_manager: ^last_version获取最新版本,但是不建议。

3.使用

按照官方文档,添加如下代码到 lib/main.dart:

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
26
27
28
29
30
31
32
33
34

import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';


//注意加async
void main() async {

///————以下部分放在runApp前

WidgetsFlutterBinding.ensureInitialized();
// 必须加上这一行。
await windowManager.ensureInitialized();

//window窗口设置
WindowOptions windowOptions = WindowOptions(
size: Size(800, 600), //长宽
center: true, //居中
backgroundColor: Colors.transparent, //背景色
skipTaskbar: false, //是否显示在任务栏
titleBarStyle: TitleBarStyle.hidden, //系统标题栏
);

//等待设置完成
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
});

//————以上部分放在runApp前


runApp(MyApp());
}

此时你就能得到一个无系统标题栏的窗口了。

4.跨平台问题

如果你使用web端运行上述例子,会发现结果一片空白。
因为windowManager自带dart.io,但是web端是不支持dart.io的。
为此我们需要按情况动态导入。

创建两个新dart文件:
desktop_setup.dar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';

//window平台执行此函数
Future<void> setupDesktop() async {
if (Platform.isWindows) {
WidgetsFlutterBinding.ensureInitialized();
await windowManager.ensureInitialized();

const windowOptions = WindowOptions(
size: Size(800, 600),
center: true,
backgroundColor: Colors.transparent,
skipTaskbar: false,
titleBarStyle: TitleBarStyle.hidden,
);

await windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
});
}
}

desktop_setup_stub.dart

1
2
3
//非window平台执行此函数
Future<void> setupDesktop() async {}


把我们的main.dart改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 条件导入,解决web平台不支持window_manager的问题
// 在window平台导入desktop_setup(包含window_manager和实际的setupDesktop函数)
// 在web平台时导入desktop_setup_stub(不包含window_manager)
import 'package:desktop_setup.dart'
if (dart.library.html) 'package:desktop_setup_stub.dart';

void main() async {

//web平台时为空函数。
setupDesktop();

runApp(MyApp());
}

这样我们就完成了两个平台的兼容了。

1
2
3
4
5
6
7
8
9
类似的也有
```dart.library.io```,在window、ios、android、linux和mac都会为true。
```dart.library.js```在浏览器和js环境会为true。

如果想做更细致的平台判断,还可以用Platform来判断平台,但是注意Platform需要```dart.io```,会导致与web端不兼容,所以也需要参照上面的方法做兼容。
``` dart
Platform.isAndroid;
Platform.isWindows;
Platform.isIOS;

5.自制标题栏

由于我们隐藏了系统的标题栏,所以需要自制一个标题栏。
关于标题栏的样式百花齐放,flutter功能很强大所以完全可以发挥想象力,不用拘泥于常见的标题栏。

但是我的例子依旧会仿照常见的标题栏。


我们利用Material的Scaffold轻松设置标题栏。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import 'package:desktop_setup.dart'
if (dart.library.html) 'package:desktop_setup_stub.dart';

void main() async {

//web平台时为空函数。
setupDesktop();

runApp(
MaterialApp(
home: Scaffold(
appBar: createTitleBar(), //自制标题栏
body: MyApp()
),
)
);
}


Widget createTitleBar(){
return Container(
height: 40,
color: Colors.grey[900], // 背景色
child: Row(
children: [
// 可拖动的空白区域
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
// 按住拖动
onPanDown: (_) => windowManager.startDragging(),
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 16),
child: const Text(
'我的应用标题',
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
),
),
// 按钮区域
IconButton(
icon: const Icon(Icons.remove, size: 18, color: Colors.white),
onPressed: () => windowManager.minimize(),
),
IconButton(
icon: const Icon(Icons.crop_square, size: 16, color: Colors.white),
onPressed: () => windowManager.maximize(), //没写还原逻辑
),
IconButton(
icon: const Icon(Icons.close, size: 18, color: Colors.white),
onPressed: () => windowManager.close(),
),
],
),
);
}


但是问题来了,我们并不希望web端和手机端出现这个标题栏.
所以老办法:

title_bar.dart

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';

//注意这里判断,只有在window平台时才返回标题栏,否则返回空。
//如果需要其他平台可以自行添加
PreferredSizeWidget? createTitleBar() {
if (!Platform.isWindows) return null;
return const _CustomTitleBar();
}

//下面就是标题栏的逻辑了。

class _CustomTitleBar extends StatelessWidget implements PreferredSizeWidget {
const _CustomTitleBar({super.key});

@override
Size get preferredSize => const Size.fromHeight(40.0);

@override
Widget build(BuildContext context) {
return Container(
height: 40,
color: Colors.grey[900], // 背景色
child: Row(
children: [
// 可拖动的空白区域
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
// 按住拖动
onPanDown: (_) => windowManager.startDragging(),
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 16),
child: const Text(
'我的应用标题',
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
),
),
// 按钮区域
IconButton(
icon: const Icon(Icons.remove, size: 18, color: Colors.white),
onPressed: () => windowManager.minimize(),
),
IconButton(
icon: const Icon(Icons.crop_square, size: 16, color: Colors.white),
onPressed: () => windowManager.maximize(),
),
IconButton(
icon: const Icon(Icons.close, size: 18, color: Colors.white),
onPressed: () => windowManager.close(),
),
],
),
);
}
}


title_bar_stub.dart

1
2
3
4
import 'package:flutter/material.dart';

// 对于 Web 和移动端,直接返回 null
PreferredSizeWidget? createTitleBar() => null;


改造main.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 条件导入createTitleBar函数
// 仅在非web端导入实际的createTitleBar函数,
// createTitleBar函数内又会再判断一次Platform.isWindows,
// 确保只在合适的平台出现标题栏
import 'title_bar.dart'
if (dart.library.html) 'title_bar_stub.dart';

void main() async {

//web平台时为空函数。
setupDesktop();

runApp(
MaterialApp(
home: Scaffold(
appBar: createTitleBar(), //自制标题栏
body: MyApp()
),
)
);
}

续一.WindowOptions的各种设置

alwaysOnTop → bool?
置顶

backgroundColor → Color?
背景色
center → bool?
居中

fullScreen → bool?
全屏

maximumSize → Size?
最大尺寸

minimumSize → Size?
最小尺寸

size → Size?
尺寸

skipTaskbar → bool?
是否在任务栏显示

title → String?
标题

titleBarStyle → TitleBarStyle?
标题栏样式

windowButtonVisibility → bool?
窗口按钮可见度

续二.WindowManager的各种方法

addListener(WindowListener listener) → void
添加监听器,监听器能监听非常多的状态。

removeListener(WindowListener listener) → void
移除监听器。

center({bool animate = false}) → Future<void>
移动窗口到中间。可开启动画。

ensureInitialized() → Future<void>
确认初始化完成。

waitUntilReadyToShow([WindowOptions? options, VoidCallback? callback]) → Future<void>
等待设置完成且可以显示。

isVisible() → Future<bool>
能否可见。

isVisibleOnAllWorkspaces() → Future<bool>
是否在所有工作区可见。

setVisibleOnAllWorkspaces(bool visible, {bool? visibleOnFullScreen}) → Future<void>
设置是否在所有工作区可见。

noSuchMethod(Invocation invocation) → dynamic
当访问不存在的方法或属性时调用。

popUpWindowMenu() → Future<void>
弹出窗口菜单。

setAlignment(Alignment alignment, {bool animate = false}) → Future<void>
按设定好的布局规则移动窗口到屏幕的某个地方。

setBackgroundColor(Color backgroundColor) → Future<void>
设置背景颜色。

setBrightness(Brightness brightness) → Future<void>
设置发光。

setIgnoreMouseEvents(bool ignore, {bool forward = false}) → Future<void>
设置窗口无视所有鼠标行为,

setProgressBar(double progress) → Future<void>
设置运行中条,0.0~1.0

startDragging() → Future<void>
开始拖动。

startResizing(ResizeEdge resizeEdge) → Future<void>
开始控制大小。


getId() → Future<int>
获取窗口id。

setBounds(Rect? bounds, {Offset? position, Size? size, bool animate = false}) → Future<void>
设置边界。

getBounds() → Future<Rect>
获取窗口的边界大小。

setAspectRatio(double aspectRatio) → Future<void>
设置比例。

getDevicePixelRatio() → double
获取设备像素比率。

setOpacity(double opacity) → Future<void>
设置透明度。

getOpacity() → Future<double>
获取窗口透明度,0.0~0.1。

setPosition(Offset position, {bool animate = false}) → Future<void>
设置窗口位置。

getPosition() → Future<Offset>
获取当前窗口的位置。

setSize(Size size, {bool animate = false}) → Future<void>
设置大小。

getSize() → Future<Size>
获取窗口宽高。

setTitle(String title) → Future<void>
设置标题。

getTitle() → Future<String>
获取窗口标题。

getTitleBarHeight() → Future<int>
获取标题栏高度。

setTitleBarStyle(TitleBarStyle titleBarStyle, {bool windowButtonVisibility = true}) → Future<void>
设置标题栏样式。


#linux
grabKeyboard() → Future<bool>
抓取键盘。

#linux
ungrabKeyboard() → Future<bool>
取消抓取键盘。


setAlwaysOnBottom(bool isAlwaysOnBottom) → Future<void>
设置永远置底。

setAlwaysOnTop(bool isAlwaysOnTop) → Future<void>
设置永远置顶。

isAlwaysOnBottom() → Future<bool>
是否永远置底。

isAlwaysOnTop() → Future<bool>
是否永远置顶。


show({bool inactive = false}) → Future<void>
显示并聚焦窗口。

hide() → Future<void>
隐藏窗口。

close() → Future<void>
尝试关闭窗口。

destroy() → Future<void>
强制关闭窗口。

setClosable(bool isClosable) → Future<void>
设置能否手动关闭。

isClosable() → Future<bool>
能否手动关闭。

setPreventClose(bool isPreventClose) → Future<void>
设置阻止关闭。

isPreventClose() → Future<bool>
关闭是否被阻止。

#window
dock({required DockSide side, required int width}) → Future<void>
锁定到任务栏。

#Windows
undock() → Future<bool>
取消收入任务栏。

isDockable() → Future<bool>
能否锁定到任务栏。

isDocked() → Future<DockSide?>
是否已经锁定到任务栏。

setSkipTaskbar(bool isSkipTaskbar) → Future<void>
设置是否显示在任务栏。

isSkipTaskbar() → Future<bool>
是否开启了出现在任务栏。

setIcon(String iconPath) → Future<void>
设置任务栏图标。

setBadgeLabel([String? label]) → Future<void>
设置任务栏标签。

focus() → Future
聚焦到窗口。

blur() → Future<void>
移除窗口聚焦。

isFocused() → Future<bool>
是否被聚焦。

setFullScreen(bool isFullScreen) → Future<void>
设置全屏。

isFullScreen() → Future<bool>
是否全屏。

setHasShadow(bool hasShadow) → Future<void>
设置阴影,windows平台下只对无边框生效。

hasShadow() → Future<bool>
窗口是否有阴影,在windows平台永远true除非窗口无边框。

setAsFrameless() → Future<void>
移除边框。删除窗口框架(标题栏、轮廓边框等),除Flutter视图之外的所有内容,可以调用setTitleBarStyle(TitleBarStyle.normal)或setTitleBar Style(title BarStyle.hidden)来恢复它。


setMaximizable(bool isMaximizable) → Future<void>
设置能否最大化。

setMaximumSize(Size size) → Future<void>
设置能否最大尺寸。

setMinimizable(bool isMinimizable) → Future<void>
设置能否最小化。

setMinimumSize(Size size) → Future<void>
设置最小尺寸。

isMaximizable() → Future<bool>
能否手动最大化。

isMaximized() → Future<bool>
是否已经最大化。

isMinimizable() → Future<bool>
能否手动最小化。

isMinimized() → Future<bool>
是否已经最小化

restore() → Future<void>
从最小化恢复到先前的尺寸。

setMovable(bool isMovable) → Future<void>
设置能否被移动。

isMovable() → Future<bool>
能否手动移动。

setResizable(bool isResizable) → Future<void>
设置能否手动设置大小。

isResizable() → Future<bool>
能否手动调整大小。

#Windows
maximize({bool vertically = false}) → Future<void>
最大化窗口。

unmaximize() → Future<void>
取消最大化。

minimize() → Future<void>
#最小化窗口


toString() → String

  • Title: Flutter桌面端窗口控制——window_manager插件的使用与跨平台问题
  • Author: Anrola
  • Created at : 2026-08-14 12:09:52
  • Updated at : 2026-08-14 16:12:44
  • Link: https://redefine.ohevan.com/2026/08/14/flutter-learn-2/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
Flutter桌面端窗口控制——window_manager插件的使用与跨平台问题