Flutter Windows平台集成插件win32_runner的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter Windows平台集成插件win32_runner的使用

win32_runner 是一个实验性的包,允许你从Dart运行器而不是默认的C++运行器运行Flutter应用程序。这对于希望在没有C++编译器的情况下创建Windows Flutter应用的人来说非常有用。

重要提示

该包目前处于实验阶段,不应用于生产应用程序。它主要是一个概念验证,并且有一些限制,例如不支持插件。

使用方法

以下是将现有Flutter应用程序在Windows上使用Dart运行器运行的步骤:

步骤1:克隆仓库

C:\src> git clone https://github.com/halildurmus/win32_runner.git
C:\src> cd win32_runner

步骤2:安装依赖

C:\src\win32_runner> dart pub get

步骤3:创建示例应用

C:\src\win32_runner> cd ..
C:\src> flutter create simpleapp
C:\src> cd simpleapp

步骤4:编译Flutter代码和资源

C:\src\simpleapp> flutter assemble -dTargetPlatform=windows-x64 --output=build -dBuildMode=release release_bundle_windows-x64_assets

步骤5:编译Dart运行器

C:\src\simpleapp> dart compile exe ..\win32_runner\example\win32_runner.dart -o win32_runner.exe

步骤6:运行Flutter应用

C:\src\simpleapp> .\win32_runner

完成这些步骤后,你应该可以看到你的Flutter应用在Windows上通过Dart运行器运行。

demo

示例代码

以下是从win32_runner示例中提取的关键代码片段:

import 'dart:ffi';
import 'dart:io';
import 'dart:math' as math;

import 'package:args/args.dart';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';
import 'package:win32_runner/win32_runner.dart';

void main() => initApp(Application.winMain);

class Application {
  static late FlutterEmbedder engine;
  static bool engineInitialized = false;

  static int mainWindowProc(int hwnd, int msg, int wParam, int lParam) {
    // 给Flutter处理窗口消息的机会。
    if (engineInitialized) {
      final result = engine.handleTopLevelWindowProc(hwnd, msg, wParam, lParam);
      if (result != FALSE) return result;
    }

    // 否则,我们处理主机窗口消息。
    switch (msg) {
      case WM_NCCREATE:
        EnableNonClientDpiScaling(hwnd);
      case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
      case WM_FONTCHANGE:
        engine.reloadSystemFonts();
        return 0;
      case WM_SIZE:
        final hostWindow = Window(hwnd);
        Window(engine.hwnd).move(hostWindow.dimensions);
        return 0;
      case WM_ACTIVATE:
        Window(engine.hwnd).setFocus();
        return 0;
      case WM_SETTINGCHANGE:
        if (wParam == 0 && lParam != 0) {
          final lParamString =
              Pointer.fromAddress(lParam).cast<Utf16>().toDartString();
          if (lParamString == 'ImmersiveColorSet') {
            Window(hwnd).updateTheme();
          }
        }
        return 0;
      case WM_DWMCOLORIZATIONCOLORCHANGED:
        Window(hwnd).updateTheme();
        return 0;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  static String parseArgs(List<String> args) {
    final parser = ArgParser()
      ..addOption('path',
          abbr: 'p',
          help: 'Relative or absolute path to a Flutter app. ',
          defaultsTo: '.')
      ..addFlag('help', abbr: 'h', help: 'Shows usage information.');
    final results = parser.parse(args);

    if (results['help'] as bool) {
      print('Runs a Flutter app.\n\nSyntax:\n${parser.usage}');
      exit(0);
    }

    final appPath = results['path'] as String;
    return Directory(appPath).absolute.path;
  }

  static void winMain(int hInstance, List<String> args, int nShowCmd) {
    final appPath = parseArgs(args);
    final iconPath = '$appPath\\windows\\runner\\resources\\app_icon.ico';
    if (!File(iconPath).existsSync()) {
      print('Icon file not found at "$iconPath".');
      exit(WIN32_ERROR.ERROR_FILE_NOT_FOUND);
    }

    CoInitializeEx(nullptr, COINIT.COINIT_APARTMENTTHREADED);
    SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);

    final windowProc = NativeCallable<WNDPROC>.isolateLocal(
      mainWindowProc,
      exceptionalReturn: 0,
    );
    final hostWindow = Window.create(
      hInstance: hInstance,
      windowCaption: 'Dart Native Win32 Window',
      className: 'FLUTTER_RUNNER_WIN32_WINDOW',
      windowProc: windowProc.nativeFunction,
      dimensions: const math.Rectangle<int>(10, 10, 1280, 720),
      iconPath: iconPath,
    )..updateTheme();

    final project = DartProject.fromRoot(appPath);
    final flutterLibrary =
        '$appPath\\windows\\flutter\\ephemeral\\flutter_windows.dll';
    if (!File(flutterLibrary).existsSync()) {
      print('`flutter_windows.dll` file not found at "$flutterLibrary".');
      exit(WIN32_ERROR.ERROR_FILE_NOT_FOUND);
    }

    // 设置Flutter视图控制器。大小必须与窗口尺寸匹配,以避免启动路径中的不必要表面创建/销毁。
    engine = FlutterEmbedder(hostWindow.dimensions, project, flutterLibrary);
    engineInitialized = true;

    Window(engine.hwnd)
      ..setParent(hostWindow)
      ..move(hostWindow.dimensions)
      ..setFocus()
      ..runMessageLoop();

    windowProc.close();
    CoUninitialize();
  }
}

更多关于Flutter Windows平台集成插件win32_runner的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter Windows平台集成插件win32_runner的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter Windows平台上集成并使用win32_runner插件的示例代码案例。win32_runner插件允许你调用Windows原生API来执行一些特定的Windows平台任务。需要注意的是,win32_runner这个名称可能是一个假设的插件名称,实际中你可能需要找到一个具体的插件或者自己使用dart:ffi来调用Windows API。

为了演示如何在Flutter Windows平台上集成并使用原生插件,这里我将展示如何调用一个简单的Windows API,比如MessageBox,这是一个显示消息框的Windows API函数。我们将使用dart:ffi来实现这个功能,因为dart:ffi允许Dart代码调用原生C函数。

步骤 1: 设置Flutter项目

首先,确保你已经创建了一个Flutter项目。如果还没有,可以使用以下命令创建:

flutter create flutter_windows_ffi_example
cd flutter_windows_ffi_example

步骤 2: 添加dart:ffi依赖

pubspec.yaml文件中,添加对ffi包的依赖:

dependencies:
  flutter:
    sdk: flutter
  ffi: ^1.0.0  # 请检查并使用最新版本

然后运行flutter pub get来获取依赖。

步骤 3: 创建Windows平台插件代码

windows目录下创建一个新的Dart文件,比如windows_api.dart,并编写以下代码:

import 'dart:ffi';
import 'package:ffi/ffi.dart';

typedef MessageBoxFunc = Int32 Function(
    Pointer<Utf16> hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, Uint32 uType);
typedef MessageBox = int Function(Pointer<Utf16>, Pointer<Utf16>, Pointer<Utf16>, int);

class WindowsApi {
  static late DynamicLibrary _user32;

  static void loadLibrary() {
    _user32 = DynamicLibrary.open('user32.dll');
  }

  static MessageBox? _messageBox;

  static MessageBox get messageBox {
    _messageBox ??= _user32
        .lookup<NativeFunction<MessageBoxFunc>>('MessageBoxW')
        .asFunction<MessageBox>();
    return _messageBox!;
  }
}

步骤 4: 调用Windows API

在你的Flutter应用的Dart代码中,比如main.dart,编写以下代码来调用MessageBox函数:

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

void main() {
  WindowsApi.loadLibrary();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Windows FFI Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              showMessageBox();
            },
            child: Text('Show Message Box'),
          ),
        ),
      ),
    );
  }

  void showMessageBox() {
    final hwnd = Pointer<Utf16>.fromAddress(0); // NULL for no parent window
    final lpText = 'Hello from Flutter!'.toNativeUtf16();
    final lpCaption = 'Flutter MessageBox'.toNativeUtf16();
    final uType = 0; // MB_OK

    WindowsApi.messageBox(hwnd, lpText, lpCaption, uType);

    // Free allocated memory
    calloc.free(lpText);
    calloc.free(lpCaption);
  }
}

步骤 5: 运行应用

确保你的开发环境已经正确配置了Flutter和Dart SDK,然后在终端中运行以下命令来构建并运行你的Flutter应用:

flutter run -d windows

当应用启动后,点击按钮应该会显示一个Windows消息框。

总结

以上代码展示了如何在Flutter Windows平台上集成并使用dart:ffi来调用Windows原生API。虽然这里使用的是MessageBox作为示例,但同样的方法可以扩展到调用其他Windows API。如果你需要更复杂的Windows平台功能,可以考虑编写一个更全面的原生插件,或者使用现有的第三方插件。

回到顶部