如何在Dart命令行应用中跨桌面平台打开窗口?
解决方案:Dart命令行应用中嵌入跨平台窗口
方案1:通过dart:ffi调用原生窗口API
直接利用Dart的FFI特性对接各平台原生窗口接口,实现轻量窗口,完全不依赖Flutter:
- Windows:调用
user32.dll创建基础窗口 - macOS:桥接Objective-C的
NSWindowAPI - Linux:调用GTK或X11的C接口
示例代码(Windows平台简化版):
import 'dart:ffi'; import 'package:ffi/ffi.dart'; // 定义user32.dll函数签名 typedef CreateWindowExC = Pointer<Void> Function( Uint32 dwExStyle, Pointer<Utf16> lpClassName, Pointer<Utf16> lpWindowName, Uint32 dwStyle, int x, int y, int nWidth, int nHeight, Pointer<Void> hWndParent, Pointer<Void> hMenu, Pointer<Void> hInstance, Pointer<Void> lpParam, ); typedef CreateWindowExDart = Pointer<Void> Function( int dwExStyle, Pointer<Utf16> lpClassName, Pointer<Utf16> lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, Pointer<Void> hWndParent, Pointer<Void> hMenu, Pointer<Void> hInstance, Pointer<Void> lpParam, ); void main() { final user32 = DynamicLibrary.open('user32.dll'); final createWindowEx = user32.lookupFunction<CreateWindowExC, CreateWindowExDart>('CreateWindowExW'); final className = 'MyDartWindow'.toNativeUtf16(); final windowName = 'Mini Window'.toNativeUtf16(); // 创建窗口 final hwnd = createWindowEx( 0, className, windowName, 0x00CF0000, // WS_OVERLAPPEDWINDOW样式 100, 100, 300, 200, nullptr, nullptr, nullptr, nullptr, ); // 显示窗口 final showWindow = user32.lookupFunction<Int32 Function(Pointer<Void>, Int32), int Function(Pointer<Void>, int)>('ShowWindow'); showWindow(hwnd, 1); // 简易消息循环 final msg = calloc<Uint8>(256); while (user32.lookupFunction<Int32 Function(Pointer<Void>), int Function(Pointer<Void>)>('GetMessageW')(msg, nullptr, 0, 0) != 0) { user32.lookupFunction<Void Function(Pointer<Void>), void Function(Pointer<Void>)>('TranslateMessage')(msg); user32.lookupFunction<Void Function(Pointer<Void>), void Function(Pointer<Void>)>('DispatchMessageW')(msg); } // 释放资源 calloc.free(className); calloc.free(windowName); calloc.free(msg); }
方案2:独立Flutter模块作为子进程启动
把窗口部分拆成独立的Flutter桌面应用,在命令行程序需要时通过子进程启动,避免污染原命令行项目的依赖环境:
- 新建极简Flutter桌面项目,只实现所需窗口UI
- 在Dart命令行应用中,通过
Process.run()启动Flutter应用的可执行文件 - 可通过命令行参数或本地Socket实现进程间通信
示例代码:
import 'dart:io'; void main() async { // 适配不同平台的Flutter可执行文件路径 final flutterWindowPath = Platform.isWindows ? r'..\flutter_window\build\windows\runner\Release\flutter_window.exe' : Platform.isMacOS ? '../flutter_window/build/macos/Build/Products/Release/flutter_window.app/Contents/MacOS/flutter_window' : '../flutter_window/build/linux/x64/release/bundle/flutter_window'; // 启动Flutter窗口进程 final process = await Process.start(flutterWindowPath, []); // 监听窗口关闭事件 process.exitCode.then((code) { print('Flutter窗口已关闭,退出码:$code'); }); }
方案3:使用轻量跨平台UI库的Dart绑定
选择libui这类轻量跨平台UI库的第三方Dart绑定,无需依赖Flutter即可创建窗口,适合简单UI场景:
示例代码(伪代码,需对应实际包调整):
import 'package:libui/libui.dart'; void main() { uiInit(); final window = uiNewWindow('Mini Window', 300, 200, 0); uiWindowOnClosing(window, (_) { uiQuit(); return 1; }); // 添加关闭按钮 final button = uiNewButton('Close'); uiButtonOnClicked(button, (_) { uiControlDestroy(button.cast()); uiWindowClose(window); }); uiWindowSetChild(window, button.cast()); uiControlShow(window.cast()); uiMain(); uiUninit(); }
避坑提示
- 不要直接在命令行项目中添加Flutter依赖,否则Dart VM会强制将程序识别为Flutter应用,导致命令行功能异常
- 使用FFI时需适配各平台API差异,建议封装跨平台适配层
- 子进程方案优势是隔离性强,无需修改原命令行项目结构,缺点是进程间通信需要额外处理
内容的提问来源于stack exchange,提问作者Joran
相关产品推荐
相关产品推荐

