Flutter Windows GUI集成插件win32_gui的使用
Flutter Windows GUI集成插件win32_gui的使用
win32_gui
是一个基于 Win32 API 的 GUI 库,它以面向对象的方式实现了一些助手功能。该库依赖于 win32
包和 dart:ffi
。
示例截图
使用方法
以下是一个简单的 “Hello World” 窗口示例:
主要代码示例
main.dart
import 'dart:io';
import 'package:win32_gui/win32_gui.dart';
Future<void> main() async {
// 自定义主窗口类(在下方声明):
var mainWindow = MainWindow(
width: 640,
height: 480,
);
// 创建窗口:
await mainWindow.create();
// 当窗口关闭并销毁时退出:
// 参见下面的 `MainWindow.processClose`:
mainWindow.onDestroy.listen((window) {
print('-- Window Destroyed> $window');
exit(0);
});
// 显示主窗口:
mainWindow.show();
// 运行Win32窗口消息循环。
await Window.runMessageLoopAsync();
}
// 自定义主窗口:
class MainWindow extends Window {
static final mainWindowClass = WindowClass.custom(
className: 'mainWindow',
windowProc: Pointer.fromFunction<WNDPROC>(mainWindowProc, 0),
bgColor: RGB(255, 255, 255),
useDarkMode: true,
titleColor: RGB(32, 32, 32),
);
static int mainWindowProc(int hwnd, int uMsg, int wParam, int lParam) =>
WindowClass.windowProcDefault(
hwnd, uMsg, wParam, lParam, mainWindowClass);
MainWindow({super.width, super.height})
: super(
defaultRepaint: false, // 告知将使用 `repaint()` 方法。
windowName: 'Win32 GUI - Example', // 窗口标题。
windowClass: mainWindowClass,
windowStyles: WINDOW_STYLE.WS_MINIMIZEBOX | WINDOW_STYLE.WS_SYSMENU,
) ;
late final String imageDartLogoPath;
late final String iconDartLogoPath;
@override
Future<void> load() async {
imageDartLogoPath = await Window.resolveFilePath(
'package:win32_gui/resources/dart-logo.bmp');
iconDartLogoPath = await Window.resolveFilePath(
'package:win32_gui/resources/dart-icon.ico');
}
@override
void build(int hwnd, int hdc) {
super.build(hwnd, hdc);
SetTextColor(hdc, RGB(255, 255, 255));
SetBkColor(hdc, RGB(96, 96, 96));
// 设置窗口图标:
setIcon(iconDartLogoPath);
}
@override
bool? processClose() {
return null; // 使用默认关闭行为(销毁窗口)
}
@override
void repaint(int hwnd, int hdc) {
var hBitmap = loadImageCached(imageDartLogoPath);
var imgDimension = getBitmapDimension(hBitmap);
if (imgDimension != null) {
var imgW = imgDimension.width;
var imgH = imgDimension.height;
final x = (dimensionWidth - imgW) ~/ 2;
final y = 10;
drawImage(hdc, hBitmap, x, y, imgW, imgH);
}
}
}
Win32 消息循环
一个 win32
应用程序需要一个消息循环。传统的消息循环会阻塞 Dart VM 循环,这会导致 Future
执行和 Isolate
消息传递中断。为了避免这个问题,可以使用 Window.runMessageLoopAsync()
函数,这样不仅能够处理 Win32 消息,还能让 Dart 代码继续执行。
await Window.runMessageLoopAsync();
如果需要运行特定时间的消息循环:
await Window.runMessageLoopAsync(timeout: Duration(seconds: 10));
或者在某个条件为真时运行:
await Window.runMessageLoopAsync(condition: () => mainWindow.isMinimized);
完整示例
以下是完整的示例,包括一些按钮和文本输出组件:
import 'dart:async';
import 'dart:io';
import 'package:win32_gui/win32_gui.dart';
import 'package:win32_gui/win32_gui_logging.dart';
Future<void> main() async {
logToConsole();
var editColors = WindowClassColors(
textColor: RGB(0, 0, 0),
bgColor: RGB(128, 128, 128),
);
WindowClass.editColors = editColors;
WindowClass.staticColors = editColors;
WindowClass.dialogColors = editColors;
var mainWindow = MainWindow(
width: 640,
height: 480,
);
print('-- mainWindow.create...');
await mainWindow.create();
print('-- mainWindow.show...');
mainWindow.show();
mainWindow.onClose.listen((window) async {
print('-- Main Window closed> $window');
print('-- Main Window isMinimized> ${mainWindow.isMinimized}');
var confirmed = mainWindow.showConfirmationDialog(
"Exit Confirmation", "Exit Application?");
if (confirmed) {
mainWindow.destroy();
} else {
mainWindow.showMessage(
'Application Status', 'Application window minimized.');
}
});
mainWindow.onDestroyed.listen((window) {
print('-- Main Window Destroyed> $window');
exit(0);
});
Timer.periodic(Duration(seconds: 1), (timer) {
print('TIMER> ${DateTime.now()} ');
});
print('-- Window.runMessageLoopAsync...');
await Window.runMessageLoopAsync();
}
class MainWindow extends Window {
static final mainWindowClass = WindowClass.custom(
className: 'mainWindow',
windowProc: Pointer.fromFunction<WNDPROC>(mainWindowProc, 0),
bgColor: RGB(255, 255, 255),
useDarkMode: true,
titleColor: RGB(32, 32, 32),
);
static int mainWindowProc(int hwnd, int uMsg, int wParam, int lParam) =>
WindowClass.windowProcDefault(
hwnd, uMsg, wParam, lParam, mainWindowClass);
late final TextOutput textOutput;
late final Button buttonOK;
late final Button buttonExit;
MainWindow({super.width, super.height})
: super(
defaultRepaint: false,
windowName: 'Win32 GUI - Example',
windowClass: mainWindowClass,
windowStyles: WINDOW_STYLE.WS_MINIMIZEBOX | WINDOW_STYLE.WS_SYSMENU,
) {
textOutput =
TextOutput(parent: this, x: 4, y: 160, width: 626, height: 250);
buttonOK = Button(
label: 'OK',
parent: this,
x: 4,
y: 414,
width: 100,
height: 32,
onCommand: _onButtonOK);
buttonExit = Button(
label: 'Exit',
parent: this,
x: 106,
y: 414,
width: 100,
height: 32,
onCommand: _onButtonExit);
}
void _onButtonOK(int w, int l) async {
print('** Button OK Click!');
var dialog = DialogExample(parent: this);
dialog.onTimeout.listen((event) {
showMessage(
'Dialog Timeout',
'Dialog Timeout> result: ${dialog.result} ; timeout: ${dialog.timeout?.inSeconds} s',
);
});
dialog.onDestroyed.listen((_) {
if (!dialog.timeoutTriggered) {
showMessage(
'Dialog Result',
'Dialog Closed> result: ${dialog.result}',
);
}
});
dialog.create();
var result = await dialog.waitAndGetResult();
print('** DialogExample result: $result');
}
void _onButtonExit(int w, int l) {
print('** Button Exit Click!');
destroy();
}
String imageDartLogoPath = '';
String iconDartLogoPath = '';
@override
Future<void> load() async {
imageDartLogoPath = await Window.resolveFilePath(
'package:win32_gui/resources/dart-logo.bmp');
print('-- imageDartLogoPath: $imageDartLogoPath');
iconDartLogoPath = await Window.resolveFilePath(
'package:win32_gui/resources/dart-icon.ico');
print('-- iconDartLogoPath: $iconDartLogoPath');
}
@override
void build(int hwnd, int hdc) {
super.build(hwnd, hdc);
SetTextColor(hdc, RGB(255, 255, 255));
SetBkColor(hdc, RGB(96, 96, 96));
setIcon(iconDartLogoPath);
setWindowRoundedCorners();
}
@override
void repaint(int hwnd, int hdc) {
var hBitmap = Window.loadImageCached(imageDartLogoPath);
var imgDimension = Window.getBitmapDimension(hBitmap);
if (imgDimension != null) {
var imgW = imgDimension.width;
var imgH = imgDimension.height;
final x = (dimensionWidth - imgW) ~/ 2;
final y = 10;
drawImage(hdc, hBitmap, x, y, imgW, imgH);
}
textOutput.callRepaint();
}
}
class TextOutput extends RichEdit {
TextOutput({super.parent, super.x, super.y, super.width, super.height})
: super(bgColor: RGB(32, 32, 32)) {
print('-- `TextOutput` default font: `$defaultFont`');
}
@override
void build(int hwnd, int hdc) {
super.build(hwnd, hdc);
setBkColor(RGB(32, 32, 32));
setTextColor(hdc, RGB(255, 255, 255));
setAutoURLDetect(true);
}
@override
void repaint(int hwnd, int hdc) {
invalidateRect();
setBkColor(RGB(32, 32, 32));
setTextColor(hdc, RGB(255, 255, 255));
setTextFormatted([
TextFormatted(" -------------------------\r\n",
color: RGB(255, 255, 255)),
TextFormatted(" Hello", color: RGB(0, 255, 255), faceName: 'Courier New'),
TextFormatted(" Word! \r\n", color: RGB(0, 255, 0)),
TextFormatted(" -------------------------\r\n",
color: RGB(255, 255, 255)),
]);
}
}
class DialogExample extends Dialog<int> {
DialogExample({required super.parent})
: super(
dialogFunction:
Pointer.fromFunction<DLGPROC>(Dialog.dialogProcDefault, 0),
title: 'Dialog Example',
x: 0,
y: 0,
width: 4 + 50 + 4 + 50 + 4,
height: 56,
timeout: Duration(seconds: 10),
items: [
DialogItem.text(
x: 10,
y: 10,
width: 100,
height: 32,
id: 0,
text: 'Yes or No? (timeout: 10s)',
),
DialogItem.button(
x: 4,
y: 30,
width: 50,
height: 22,
id: 1,
text: 'Yes',
),
DialogItem.button(
x: 4 + 50 + 4,
y: 30,
width: 50,
height: 22,
id: 2,
text: 'No',
),
]);
late final String iconDartLogoPath;
@override
Future<void> load() async {
iconDartLogoPath = await Window.resolveFilePath(
'package:win32_gui/resources/dart-icon.ico');
print('-- iconDartLogoPath: $iconDartLogoPath');
}
@override
void build(int hwnd, int hdc) {
setIcon(iconDartLogoPath);
}
}
这个示例展示了如何创建一个带有按钮和文本输出的窗口,并且包含了一个对话框的例子。希望这些信息对你有所帮助!
更多关于Flutter Windows GUI集成插件win32_gui的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter Windows GUI集成插件win32_gui的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中集成并使用win32_gui
插件来与Windows原生GUI进行交互的示例代码案例。这个插件允许你调用Windows API来进行底层窗口和控件的操作。
首先,确保你已经安装了win32_gui
插件。你可以在你的pubspec.yaml
文件中添加以下依赖项:
dependencies:
flutter:
sdk: flutter
win32_gui: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来是一个简单的Flutter应用示例,它使用win32_gui
插件来创建一个Windows窗口,并显示一个按钮和一个文本框。
import 'package:flutter/material.dart';
import 'package:win32_gui/win32_gui.dart';
import 'dart:ffi';
import 'dart:typed_data';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Win32 GUI Example'),
),
body: Center(
child: ElevatedButton(
onPressed: _createWin32Window,
child: Text('Create Win32 Window'),
),
),
),
);
}
void _createWin32Window() {
// 使用win32_gui插件的初始化方法
Win32Gui.initialize();
// 定义窗口过程函数
Pointer<NativeFunction<WindowProc>> windowProcPointer =
Pointer.fromFunction<WindowProc>(windowProc, 'windowProc');
// 注册窗口类
final className = 'MyWindowClass';
final hInstance = GetModuleHandle(null)!;
RegisterClassEx(
WNDCLASSEX(
cbSize: sizeof<WNDCLASSEX>(),
style: 0,
lpfnWndProc: windowProcPointer,
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: hInstance,
hIcon: LoadIcon(hInstance, IDI_APPLICATION),
hCursor: LoadCursor(null, IDC_ARROW),
hbrBackground: reinterpretCast<IntPtr>(COLOR_WINDOW + 1),
lpszMenuName: null,
lpszClassName: className.toNativeUtf16(),
hIconSm: LoadIcon(hInstance, IDI_APPLICATION),
),
);
// 创建窗口
final hwnd = CreateWindowEx(
0,
className.toNativeUtf16(),
'My Win32 Window'.toNativeUtf16(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
null,
null,
hInstance,
null,
);
// 显示窗口
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// 定义窗口过程函数逻辑
WindowProc windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 50, 50, 'Hello, Win32 GUI!'.toNativeUtf16(), 17);
EndPaint(hwnd, &ps);
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
}
}
// 以下是必要的Windows API函数和结构体定义
// 注意:这些定义需要根据实际使用的win32_gui插件提供的API进行适当调整
typedef HWND = IntPtr;
typedef UINT = Uint32;
typedef WPARAM = UintPtr;
typedef LPARAM = IntPtr;
typedef HWND CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class WNDCLASSEX extends Struct {
@Uint32()
int cbSize;
@Uint32()
int style;
Pointer<NativeFunction<WindowProc>> lpfnWndProc;
@Uint32()
int cbClsExtra;
@Uint32()
int cbWndExtra;
@IntPtr()
int hInstance;
@IntPtr()
int hIcon;
@IntPtr()
int hCursor;
@IntPtr()
int hbrBackground;
Pointer<Utf16> lpszMenuName;
Pointer<Utf16> lpszClassName;
@IntPtr()
int hIconSm;
}
// 调用Windows API的函数(这些函数需要在win32_gui插件中定义或通过ffi加载)
external IntPtr GetModuleHandle(Pointer<Utf8>? lpModuleName);
external bool RegisterClassEx(Pointer<WNDCLASSEX> lpWndClassEx);
external HWND CreateWindowEx(
int dwExStyle,
Pointer<Utf16> lpClassName,
Pointer<Utf16> lpWindowName,
int dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
Pointer<Utf16>? hMenu,
IntPtr hInstance,
Pointer<Void>? lpParam);
external bool ShowWindow(HWND hWnd, int nCmdShow);
external bool UpdateWindow(HWND hWnd);
external int DefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
external int BeginPaint(HWND hWnd, Pointer<PAINTSTRUCT> lpPaint);
external int EndPaint(HWND hWnd, Pointer<PAINTSTRUCT> lpPaint);
external bool TextOut(IntPtr hdc, int x, int y, Pointer<Utf16> lpString, int c);
external int PostQuitMessage(int nExitCode);
// 常量
const int WS_OVERLAPPEDWINDOW = 0xCF0000;
const int CW_USEDEFAULT = 0x80000000;
const int WM_DESTROY = 0x0002;
const int WM_PAINT = 0x000F;
const int SW_SHOW = 0x0005;
const int IDI_APPLICATION = 32512;
const int IDC_ARROW = 32512;
const int COLOR_WINDOW = 5;
// FFI类型转换函数
Pointer<Utf16> toNativeUtf16(String str) {
Uint16List utf16 = str.codeUnits;
Pointer<Uint16> ptr = allocate<Uint16List>(utf16.length + 1);
ptr.setAll(0, utf16);
ptr.valueAt(utf16.length) = 0; // Null-terminate the string
return ptr.cast<Utf16>();
}
注意:
- 示例代码中的Windows API函数和结构体定义需要根据实际使用的
win32_gui
插件提供的API进行适当调整。win32_gui
插件可能已经封装了一些常见的Windows API调用,因此你可能不需要直接调用FFI函数。 - 示例代码中的
_createWin32Window
函数是异步的,但在这个例子中并没有处理异步逻辑。在实际应用中,你可能需要使用Dart的异步机制(如Isolate
或Future
)来确保UI线程不会阻塞。 - 示例代码中的错误处理和资源释放(如释放内存和注销窗口类)没有详细实现,请在实际应用中添加必要的错误处理和资源释放代码。
由于win32_gui
插件的具体API和实现可能会有所不同,建议查阅该插件的官方文档或源代码以获取更详细和准确的信息。