Flutter开发windows应用(二)多窗口实现
2024-01-13 本文已影响0人
非新生代菜鸟
Flutter开发windows桌面应用实现多窗口
相信许多开发者在使用flutter开发windows应用程序的时候,始终有一个逃避不开的问题,那就是应用多窗口。
官方早在2020年就写入了multi_window计划,并在2022年补充了multi_view计划,但时至2024,依然没有给大家带来惊喜。
今天我们就来说一说如何利用pub.dev上现有的一些库来实现我们的windows多窗口。
- 首先我们在pubspec.yaml中引入:
desktop_multi_window: ^0.2.0
#desktop_lifecycle: ^0.1.1 #自己管理新窗口状态,所以暂时未使用它
window_manager: ^0.3.4
- windows\flutter_window.cpp中FlutterWindow::OnCreate(){},手动注册flutter_engine及引入的库
// 引入用到的头文件
#include "desktop_multi_window/desktop_multi_window_plugin.h"
#include "desktop_lifecycle/desktop_lifecycle_plugin.h"
#include "window_manager/window_manager_plugin.h"
int findAndHideWindow(){
// 通过窗口标题查找窗口句柄
const char* windowTitle = "CrossMaskLayer";
HWND hWnd = FindWindowA(NULL, windowTitle);
if (hWnd != NULL) {
// 找到窗口,设置窗口样式为 WS_EX_TOOLWINDOW , 忽略鼠标事件WS_EX_TRANSPARENT,不在任务栏显示WS_EX_TOOLWINDOW
SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT);
} else {
// 未找到窗口
MessageBoxA(NULL, "Window not found.", "Error", MB_OK | MB_ICONERROR);
}
return 0;
}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// flutter 主窗口引擎============================================================================
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
// channel创建主窗口中与原生Native通信的Native_method_channel,实现主窗口的原生能力
flutter::MethodChannel<> channel(
flutter_controller_->engine()->messenger(), "my_native_method_channel",
&flutter::StandardMethodCodec::GetInstance());
channel.SetMethodCallHandler(
[](const flutter::MethodCall<> &call,
std::unique_ptr <flutter::MethodResult<>> result) {
if (call.method_name() == "doSth") {
int res = doSth();
result->Success(res);
} else {
result->NotImplemented();
}
});
SetChildContent(flutter_controller_->view()->GetNativeWindow());
// flutter 子窗口引擎 ===========================================================================
DesktopMultiWindowSetWindowCreatedCallback([](void *controller) {
auto *flutter_controller_sub_ =
reinterpret_cast<flutter::FlutterViewController *>(controller);
auto *registry = flutter_controller_sub_->engine();
// 在子窗口引擎中注册DesktopLifecyclePlugin
DesktopLifecyclePluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopLifecyclePlugin"));
// 在子窗口引擎中注册WindowManagerPlugin
WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
// 在子窗口引擎中subChannel创建一个在子窗口中可以使用的native_method_channel,实现原生调用
flutter::MethodChannel<> subChannel(
flutter_controller_sub_->engine()->messenger(), "sub_native_method_channel",
&flutter::StandardMethodCodec::GetInstance());
subChannel.SetMethodCallHandler(
[](const flutter::MethodCall<> &call,
std::unique_ptr <flutter::MethodResult<>> result) {
if (call.method_name() == "hideSubWindow") {
int res = findAndHideWindow();
result->Success(res);
} else if (call.method_name() == "closeSubWindow") {
int res = closeSubWindow();
result->Success(res);
} else {
result->NotImplemented();
}
});
});
return true;
}
- dart层定义全局常量
import 'package:desktop_multi_window/desktop_multi_window.dart';
// 创建主窗口平台通道
const platform = MethodChannel('my_native_method_channel');
// 创建子窗口平台通道
const subPlatform = MethodChannel('sub_native_method_channel');
// 多窗口控制器,这里我们只用到一个新窗口,所以固定windowId=1;
final subWindowController = WindowController.fromWindowId(1);
- 在main.dart中,编写入口逻辑:
import 'package:window_manager/window_manager.dart';
import 'package:window_manager/window_manager.dart' as subWindowManager;
import 'pages/subWindow.dart';
void main(List<String> args) async {
// main函数中对窗口初始化模式进行控制
if (args.firstOrNull == 'multi_window') {
final windowId = int.parse(args[1]);
Log.i('windowId:$windowId');
final argument = args[2].isEmpty ? const {} : jsonDecode(args[2]) as Map<String, dynamic>;
WidgetsFlutterBinding.ensureInitialized();
await subWindowManager.windowManager.ensureInitialized();
WindowOptions wnwOptions = const WindowOptions(
size: Size(200, 100),
backgroundColor: Colors.transparent,
skipTaskbar: false,
titleBarStyle: TitleBarStyle.hidden,
title: "CrossMaskLayer",
windowButtonVisibility: false,
alwaysOnTop: true,
);
subWindowManager.windowManager.waitUntilReadyToShow(wnwOptions, () async {
subWindowManager.windowManager.setAsFrameless(); //无边框
subWindowManager.windowManager.setHasShadow(false); //这里不能有阴影,否则会出现一个透明外框
subWindowManager.windowManager.setMaximizable(false);
subWindowManager.windowManager.setMinimizable(false);
subWindowManager.windowManager.setResizable(false);
//subWindowManager.windowManager.setAlwaysOnTop(true);
//subWindowManager.windowManager.setClosable(false);
//subWindowManager.windowManager.setIgnoreMouseEvents(true,forward: true);
await subWindowManager.windowManager.show();
// await subWindowManager.windowManager.focus();
});
runApp(SubWindow(
windowController: subWindowController, // 前面定义的全局常量,方便后面引用
args: argument,
));
} else {
WidgetsFlutterBinding.ensureInitialized();
await windowManager.ensureInitialized();
WindowOptions windowOptions = const WindowOptions(
size: Size(1200, 700),
backgroundColor: Colors.transparent,
skipTaskbar: false,
titleBarStyle: TitleBarStyle.hidden,
title: "主窗口",
windowButtonVisibility: false,
// alwaysOnTop: true,
center: true,
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
windowManager.setAsFrameless(); //无边框
windowManager.setHasShadow(false); //这里不能有阴影,否则会出现一个透明外框
windowManager.setMaximizable(false);
windowManager.setResizable(false);
// windowManager.setAlwaysOnTop(true);
await windowManager.show();
await windowManager.focus();
});
runApp(const BaseApp());
}
}
- SubWindow.dart
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:window_manager/window_manager.dart' as subWindowManager;
import '../common/global.dart';
import '../common/utils/log.dart';
class SubWindow extends StatefulWidget {
const SubWindow({
super.key,
required this.windowController,
required this.args,
});
final WindowController windowController;
final Map? args;
@override
State<SubWindow> createState() => _SubWindowState();
}
class _SubWindowState extends State<SubWindow> with subWindowManager.WindowListener {
@override
void initState() {
super.initState();
// WidgetsBinding.instance.addPostFrameCallback((_) {
// subPlatform.invokeMethod('hideSubWindow');
// });
Future.delayed(const Duration(seconds: 1), () {
// 延迟1秒调用subChannel原生通道,隐藏状态栏中的新窗口,忽略鼠标事件
subPlatform.invokeMethod('hideSubWindow');
});
subWindowManager.windowManager.addListener(this);
_init();
}
@override
void onWindowClose() {
Log.i('sub window on close');
// 修改子窗口状态为可以创建
homeController.changeSubWndRunningState(0);
}
@override
void dispose() {
subWindowManager.windowManager.removeListener(this);
super.dispose();
}
@override
void onWindowFocus() {
// Make sure to call once.
setState(() {});
// do something
}
void _init() async {
// Add this line to override the default close handler
await subWindowManager.windowManager.setPreventClose(true);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Container(
width: 40,
height: 40,
color: Colors.transparent,
alignment: Alignment.center,
child: SvgPicture.asset(
'assets/images/cross/plus.svg',
width: 40,
height: 40,
),
);
}
}
- 在HomeController.dart中,定义新窗口状态变量及set方法
// 子窗口状态码 0未打开,1运行中,2已隐藏
int subWndRunning = 0;
// 子窗口是否已经运行中
void changeSubWndRunningState(int v) {
subWndRunning = v;
update();
}
- 在Home.dart页面中调用按钮点击事件,实现创建和隐藏新窗口等操作
@override
void dispose() {
windowManager.removeListener(this);
// 关闭新窗口
subWindowController.close(); // 新窗口控制器
super.dispose();
}
Future<void> showMaskWindow() async {
final window = await DesktopMultiWindow.createWindow(jsonEncode({
'args0': 'multi_window',
'args1': 1,
}));
window
..setFrame(const Offset(0, 0) & const Size(200, 100))
..center();
// 注意这里不能使用..show(),因为此能力需要交给main.dart中的window_manager,否则会有一个带toolbar的窗口。
// ..show();
}
GetBuilder<HomeController>(
init: HomeController(),
builder: (__) {
return GestureDetector(
onTap: () {
if (__.subWndRunning == 0) {
Log.i('子窗口可以创建');
showMaskWindow();
homeController.changeSubWndRunningState(1);
} else if (__.subWndRunning == 1) {
Log.i('子窗口可以隐藏');
subWindowController.hide();
homeController.changeSubWndRunningState(2);
} else {
Log.i('子窗口可以显示');
subWindowController.show();
homeController.changeSubWndRunningState(1);
}
},
child:Text('点我打开/隐藏新窗口'),
},
),
最后:希望可以给想用flutter开发windows桌面应用的朋友带来一点帮助吧,期待官方的多窗口支持早点出来