Visitar URL original
window_manager/README-ZH.md at main · Otzaria/window_manager · GitHub
Skip to content

Latest commit

 

History

History
330 lines (272 loc) · 22 KB

README-ZH.md

File metadata and controls

330 lines (272 loc) · 22 KB

window_manager 现在构建在 nativeapi 之上, 它是 C++ 核心库 libnativeapi/nativeapi 的 Flutter 绑定, macOS、Windows、Linux 共用同一份实现。从 0.5.x 升级?请看从 0.5.x 升级。

问题反馈: 请统一到 nativeapi issues 提交问题和功能需求,并注明包名、版本、操作系统,附上最小复现示例。

window_manager

pub version All Contributors

这个插件让 Flutter 桌面应用可以调整、移动、显示、隐藏和装饰自己的窗口。

English | 简体中文


平台支持

Linux macOS Windows
✔️ ✔️ ✔️

快速开始

安装

把它加到你的 pubspec.yaml 里:

dependencies:
  window_manager: ^0.6.0

或者

dependencies:
  window_manager:
    git:
      url: https://github.com/leanflutter/window_manager.git
      ref: main

环境要求

  • Flutter 3.47 / Dart 3.13 及以上,macOS 10.15 及以上。
  • Linux 构建机需要 GTK 3、X11 和 Xi 的开发文件:
sudo apt-get install libgtk-3-dev libx11-dev libxi-dev

用法

import 'package:window_manager/window_manager.dart';

final window = WindowManager.instance.getCurrent()!;

window.title = 'window_manager';
window.setSize(const Size(1000, 700).toNative(), false);
window.minimumSize = const Size(640, 480).toNative();
window.center();
window.show();

// 本应用所有窗口的所有窗口事件。
WindowManager.instance.addListener((event) {
  switch (event) {
    case WindowFocusedEvent():
      debugPrint('window ${event.windowId} focused');
    case WindowResizedEvent():
      debugPrint('window ${event.windowId} is now ${event.newSize.toSize()}');
    default:
      break;
  }
});

// 无论用户怎样关闭,关闭这个窗口前先询问。关闭请求只会发给窗口自己的监听器。
window.addListener((event) async {
  if (event is WindowCloseRequestedEvent && !await confirmClose()) {
    event.request.cancel();
  }
});

Window 使用的是 nativeapi 的 Size、Point、Rectangle、Color,它们和 Flutter 的同名类型冲突,所以没有导出:用 toNative() 把 Flutter 的值转过去,用 toSize()、 toOffset()、toRect()、toColor() 转回来。

使用 Flutter 的多窗口 API 时,package:window_manager/windowing.dart 可以拿到每个 Flutter 窗口控制器背后的 Window:

import 'package:window_manager/windowing.dart';

final window = controller.nativeWindow; // 或者 nativeWindowOf(controller)
window?.title = 'Inspector';

隐藏系统标题栏,改由 widget 来拖动和缩放:

final window = WindowManager.instance.getCurrent()!;
window.titleBarStyle = TitleBarStyle.hidden;

// 在应用里:
DragToResizeArea(
  child: Column(
    children: [
      WindowCaption(
        brightness: Theme.of(context).brightness,
        title: const Text('window_manager'),
      ),
      Expanded(child: body),
    ],
  ),
)

本插件的示例应用演示的是 0.5.x 兼容 API。完整示例——同时管理多个窗口、 父子窗口、每一个原生属性——在 nativeapi 的 window_example。

从 0.5.x 升级

为 window_manager 0.5.x 写的代码,只要把导入从 package:window_manager/window_manager.dart 换成 package:window_manager/legacy.dart 就能继续工作:老的 windowManager、 WindowListener、WindowOptions 和 calcWindowPosition 都在那里,底层换成了原生 API。

导入路径特意要改:legacy.dart 是一座桥,不是这个包的未来。里面的类都标了 @Deprecated,会在之后的版本里删掉——能迁就迁到上面的原生 API。

import 'package:window_manager/legacy.dart';

await windowManager.ensureInitialized();
await windowManager.waitUntilReadyToShow(
  const WindowOptions(size: Size(1000, 700), center: true),
  () async {
    await windowManager.show();
    await windowManager.focus();
  },
);

和 0.5.x 的差别:

  • 需要 Flutter 3.47 / Dart 3.13 和 macOS 10.15(0.5.x 是 Flutter 3.3、macOS 10.11), 但不再需要在 MainFlutterWindow.swift、my_application.cc 或 Windows runner 里做插件 自己的接入——已经没有任何平台插件代码了。
  • close()、destroy()、setPreventClose() 的行为和以前一样:设了 setPreventClose(true) 之后,标题栏的关闭按钮和 close() 都会报告 onWindowClose 而不关闭窗口,destroy() 仍然会关掉它。关闭最后一个窗口时是否退出应用,由平台的 runner 决定。 未开启 preventClose 时也会报告 onWindowClose。destroy() 跳过兼容层的回调和否决, 其他原生监听器仍可取消关闭请求。
  • onWindowResized、onWindowMoved 和 onWindowResize、onWindowMove 一起到达: nativeapi 每次变化只报一个事件,没有"过程中"和"结束时"之分。
  • Windows 的 isDockable、isDocked、dock、undock 通过系统 appbar 预留桌面工作区, 并触发 onWindowDocked / onWindowUndocked。取消停靠恢复原来的窗口尺寸和位置, 原生窗口销毁时释放预留空间。这个功能独立于 Windows 的贴靠布局。
  • grabKeyboard() / ungrabKeyboard() 在 Linux X11 上独占和释放键盘;Wayland 和其他平台 返回 false。窗口隐藏或销毁时释放独占。
  • popUpWindowMenu() 在 Windows 和提供系统菜单的 Linux 窗口管理器上会在光标处弹出系统菜单, 在 macOS 上什么都不做。
  • setIgnoreMouseEvents 的 forward 在平台支持时(macOS、Windows、X11)保留鼠标悬停事件; 其他平台上窗口直接忽略鼠标,不转发。
  • Windows 支持 maximize(vertically: true),双击上下调整大小边缘会切换垂直最大化。 macOS 支持 setPosition / setBounds 的 animate,以及 setVisibleOnAllWorkspaces 的 visibleOnFullScreen。不支持原生窗口动画的平台直接应用几何变化。
  • windowButtonVisibility 也控制 Windows 和 Linux 的原生标题栏按钮,隐藏/显示整组按钮时 保留各按钮的独立策略。Windows 上 setClosable(false) 只禁用关闭命令,保留其他按钮; Linux 服务端装饰是否响应取决于窗口管理器。
  • Linux 保留首次显示前的全屏、最小化请求,并支持取消;退出启动全屏后恢复请求的普通窗口宽度。
  • getId() 保持 macOS 窗口服务器编号和 Windows HWND 的旧语义。原生 API 的查询和事件 使用 window.id;平台互操作使用 macOS 的 window.nativeNumber 或 Windows 的 window.nativeObject。
  • setAsFrameless() 通过 window.setFrameless(true) 去除边框。显式设置任一标题栏样式都会恢复边框。 macOS 兼容层保留旧版的透明背景和阴影默认值;原生无边框策略独立保留阴影偏好。
  • macOS 的 setMaximizable(false) 拒绝原生 zoom,不再连带禁用边缘缩放。
  • setAlignment 支持任意 Alignment,不再只认那九个常量。
  • WindowCaption 的关闭按钮会关闭窗口,并先经过窗口的关闭监听器,所以 setPreventClose 对它同样有效。在 Windows 11 上,它的最大化按钮会弹出贴靠布局。
  • Windows 全屏时仍忽略 startDragging()、startResizing(),保持 0.5.x 行为;此限制和 原生窗口所属线程上的派发由 core 处理。
  • screen_retriever 和 path 不再是依赖。
  • 新示例只用 package:flutter/widgets.dart;完整示例是 nativeapi 的 window_example。

迁移到原生 API

0.5.x(legacy.dart) 原生 API(window_manager.dart)
windowManager(应用的那一个窗口) WindowManager.instance.getCurrent()——还有 get(id)、getAll()、getWindowAtPoint();每个窗口都是独立的 Window
await windowManager.getSize()、setSize(size) window.size.toSize()、window.setSize(size.toNative(), animate)——同步,不需要 await
getBounds() / setBounds(rect) window.bounds.toRect()、window.bounds = rect.toNative();不含装饰的区域用 window.contentBounds
getPosition() / setPosition(offset) window.position.toOffset()、window.position = offset.toNative()
setMinimumSize / setMaximumSize / setAspectRatio window.minimumSize、window.maximumSize、window.aspectRatio
show() / show(inactive: true) / hide() window.show() / window.showInactive() / window.hide()
focus()、blur()、isFocused() window.focus()、window.blur()、window.isFocused
maximize()、unmaximize()、minimize()、restore() window 上的同名方法,不需要 await
setFullScreen(bool) / isFullScreen() window.isFullScreen
setResizable、setMovable、setMinimizable、setMaximizable、setClosable window.isResizable、isMovable、isMinimizable、isMaximizable、isClosable——还有 isFullScreenable
setAlwaysOnTop / setAlwaysOnBottom window.isAlwaysOnTop / window.isAlwaysOnBottom
setSkipTaskbar(true) window.isVisibleInTaskbar = false
setTitle / getTitle window.title
setAsFrameless() window.setFrameless(true),同时隐藏标题栏和原生按钮
getId() macOS 的 window.nativeNumber;Windows 的 window.nativeObject.address
setTitleBarStyle(style, windowButtonVisibility:) window.titleBarStyle、window.isWindowControlButtonsVisible——还有 setTitleBarColors()
setHasShadow、setOpacity、setBackgroundColor window.hasShadow、window.opacity、window.backgroundColor——还有 window.visualEffect
setIgnoreMouseEvents(ignore, forward:) window.setIgnoreMouseEvents(ignore, forward)
close() / destroy() window.close(),会先询问窗口的关闭监听器
setPreventClose(true) 配合 onWindowClose window.addListener((event) { if (event is WindowCloseRequestedEvent) event.request.cancel(); })
popUpWindowMenu() window.showSystemMenu(position)——用 Window.isSystemMenuSupported() 判断平台是否支持
setVisibleOnAllWorkspaces(bool) window.isVisibleOnAllWorkspaces
visibleOnFullScreen: window.setVisibleOnFullScreen(bool)、window.isVisibleOnFullScreen
setBounds(rect, animate: true) window.animateBounds(rect.toNative());返回 false 时直接设置 window.bounds
maximize(vertically: true) window.setMaximizedVertically(true) / false、window.isMaximizedVertically
dock(side:, width:)、undock()、isDocked() window.dock(WindowDockSide.left, width)、window.undock()、window.dockSide
grabKeyboard()、ungrabKeyboard() window 上的同名方法;Window.isKeyboardGrabSupported() 查询当前显示服务是否支持
startDragging() / startResizing(edge) window 上的同名方法;DragToMoveArea、DragToResizeArea 已经替你调用了
setProgressBar、setBadgeLabel、setIcon、setBrightness Application.instance——它们属于应用,不属于某一个窗口
WindowListener 单个窗口用 window.addListener((event) { switch (event) { case WindowFocusedEvent(): … } }),所有窗口用 WindowManager.instance.addListener——还有 WindowBlurredEvent、WindowMinimizedEvent、WindowMaximizedEvent、WindowRestoredEvent、WindowMovedEvent、WindowResizedEvent、WindowEnteredFullScreenEvent、WindowExitedFullScreenEvent、WindowCreatedEvent、WindowClosedEvent
calcWindowPosition(size, alignment) DisplayManager.instance——getAll()、getPrimary()、getCursorPosition(),以及每个 Display 的 workArea

waitUntilReadyToShow、ensureInitialized、getDevicePixelRatio 没有对应的原生 API:直接设好需要的属性,准备好了再调 window.show()。

文章

谁在使用

  • AuthPass - Password Manager based on Flutter for all platforms. Keepass 2.x (kdbx 3.x) compatible.
  • Biyi (比译) - A convenient translation and dictionary app written in dart / Flutter.
  • BlueBubbles - BlueBubbles is an ecosystem of apps bringing iMessage to Android, Windows, and Linux
  • LunaSea - A self-hosted controller for mobile and macOS built using the Flutter framework.
  • Linwood Butterfly - Open source note taking app written in Flutter
  • RustDesk - Yet another remote desktop software, written in Rust. Works out of the box, no configuration required.
  • Ubuntu Desktop Installer - This project is a modern implementation of the Ubuntu Desktop installer.

API

原生 API

window_manager 现在直接重新导出 nativeapi 的窗口 API——Window、WindowManager、 TitleBarStyle、ResizeEdge、VisualEffect、各种窗口事件以及用于响应关闭请求的 EventRequest,还有 nativeapi_flutter 的 DragToMoveArea、DragToResizeArea、 MaximizeButtonArea 三个 widget 和类型转换——再加上它自己的 WindowCaption、 WindowCaptionButton 和 VirtualWindowFrame。只有还在用 0.5.x API 的代码才需要导入 package:window_manager/legacy.dart。

Contributors

LiJianying
LiJianying

💻
 A Arif A S
A Arif A S

💻
J-P Nurmi
J-P Nurmi

💻
Dixeran
Dixeran

💻
nikitatg
nikitatg

💻
Kristen McWilliam
Kristen McWilliam

💻
Kingtous
Kingtous

💻
Prome
Prome

💻
Bin
Bin

💻
youxiachai
youxiachai

💻
Allen Xu
Allen Xu

💻
CodeDoctor
CodeDoctor

💻
Jean-Christophe Binet
Jean-Christophe Binet

💻
Jon Salmon
Jon Salmon

💻
Karol Wrótniak
Karol Wrótniak

💻
LAIIIHZ
LAIIIHZ

💻
Mikhail Kulesh
Mikhail Kulesh

💻
Prateek Sunal
Prateek Sunal

💻
Ricardo Boss
Ricardo Boss

💻
Add your contributions

许可证

MIT