Flutter Web技术集成插件web_ffi_fork的使用

Flutter Web技术集成插件web_ffi_fork的使用

web_ffi

web_ffi 是一个用于在Web上使用 dart:ffi 的即插即用解决方案。这使得你能够更方便地与 WebAssembly 进行交互。

基本思路是暴露一个与 dart:ffi 兼容的API,但所有的调用都通过 dart:js 转换到运行在浏览器上的 WebAssembly

目前,只有使用 emscripten 编译的 WebAssembly 才可用,因为 emscripten 也生成了 WebAssembly 需要的JavaScript导入。如果你认为我们应该支持其他平台或编译器,请在 GitHub 上提交一个issue。

对于如何使用该包(包括 emscripten 的编译设置)的教程,请参见 example/README,但在阅读此教程之前请务必先阅读此README!

与dart:ffi的区别

虽然 web_ffi 尽可能地模仿了 dart:ffi 的API,但仍有一些差异。以下是最重要的几点,务必阅读。更多细节请参阅API文档。

  • web_ffi 设计时考虑到了 <a href="https://api.dart.dev/stable/2.12.0/dart-ffi/dart-ffi-library.html">dart:ffi API 2.12.0</a>,因此当前没有数组扩展(它们是在dart 2.13.0中引入的)
  • 目前不支持结构体(但提供了不透明的结构体)
  • 有些类和函数在 web_ffi 中存在但在 dart:ffi 中不存在;这些事物被注解为 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_meta/extra-constant.html">@extra</a>
  • 有一个新的类 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/Memory-class.html">Memory</a>,非常重要,并将在后面详细解释。
  • <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/DynamicLibrary-class.html">DynamicLibrary</a> 类构造函数不同,需要一个 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/Module-class.html">@extra Module</a> 类的实例。
  • 如果你扩展了 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/Opaque-class.html">Opaque</a> 类,必须在使用之前使用 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/registerOpaqueType.html">@extra registerOpaqueType&lt;T&gt;()</a> 注册该扩展类!另外,你的类不能有类型参数(这应该不是问题)。
  • 有关与本地函数交互的规则如下:

函数规则

在处理函数时,有一些规则和需要注意的事情:

  • 当使用 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/DynamicLibrary/lookup.html">DynamicLibrary.lookup&lt;NativeFunction&lt;NF&gt;&gt;()</a>(或 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/DynamicLibraryExtension/lookupFunction.html">DynamicLibraryExtension.lookupFunction&lt;T extends Function, F extends Function&gt;()</a>)查找函数时,实际的类型参数 <NF>(或 <T> 分别)未被使用:没有类型检查,只要从 <WebAssembly> 导出的函数具有相同的签名或参数数量,只根据名称查找。
  • 如果你调用 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/NativeFunctionPointer/asFunction.html">NativeFunctionPointer.asFunction&lt;DF&gt;()</a>(或 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/DynamicLibraryExtension/lookupFunction.html">DynamicLibraryExtension.lookupFunction&lt;T extends Function, F extends Function&gt;()</a>,它内部使用前者),则对函数的返回类型(而不是参数类型)有一些特殊约束:
    • 你可以将指针类型嵌套最多两次,但不能再多:
      • 例如,Pointer&lt;Int32&gt;Pointer&lt;Pointer&lt;Int32&gt;&gt; 是允许的,但 Pointer&lt;Pointer&lt;Pointer&lt;Int32&gt;&gt;&gt; 是不允许的。
    • 如果返回类型是 Pointer&lt;NativeFunction&gt;,你必须使用 Pointer&lt;NativeFunction&lt;dynamic&gt;&gt;,否则会失败。你可以之后自己进行类型转换。另一方面,如前所述,NativeFunction 的类型参数实际上会被忽略。
    • 为了具体化上述内容,<a href="https://github.com/EPNW/web_ffi/tree/master/return_types.md" rel="ugc">return_types.md</a> 列出了可以使用的返回类型,其他任何类型都会导致运行时错误。
    • 工作区:如果你需要其他东西(例如 Pointer&lt;Pointer&lt;Pointer&lt;Double&gt;&gt;&gt;),可以使用 Pointer&lt;IntPtr&gt; 并在之后自行进行类型转换。

内存

当你想要使用 web_ffi 时,你应该首先调用 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/Memory/init.html">Memory.init()</a>。它有一个可选参数,你可以在其中调整指针大小。默认参数为4以表示32位指针,如果你使用的是wasm64,则应调用 Memory.init(8)

dart:ffi 不同,dart 进程共享所有内存,而在 WebAssembly 中,每个实例都绑定到一个 <WebAssembly.Memory> 对象。目前,我们假设你使用的每个 <WebAssembly> 模块都有自己的内存。如果你认为我们应该改变这一点,请在 GitHub 上打开一个issue并报告你的用例。

你使用的每个指针都绑定到一个内存对象。可以通过 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/Pointer/boundMemory.html">@extra Pointer.boundMemory</a> 字段访问此内存对象。如果你想要使用 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/Pointer/Pointer.fromAddress.html">Pointer.fromAddress()</a> 构造函数创建一个指针,你可能会注意到可选的 <code>bindTo</code> 参数。由于每个指针都必须绑定到一个内存对象,你可以在这里明确指定一个内存对象。为了匹配 <dart:ffi> API,bindTo 参数是可选的。因为它可选,所以如果没有指定 <bindTo>,必须有一个回退机制:静态的 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/Memory/global.html">@extra Memory.global</a> 字段。如果该字段也没有设置,当调用 <Pointer.fromAddress()> 构造函数时会抛出异常。

此外,每个 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/DynamicLibrary-class.html">DynamicLibrary</a> 都绑定到一个内存对象,可以通过 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/DynamicLibrary/boundMemory.html">@extra DynamicLibrary.boundMemory</a> 访问。这可能会很有用,因为 <Memory> 实现了 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/Allocator-class.html">Allocator</a> 类。

完整示例代码

1. 编写代理文件

代理文件是一个简单的dart文件,它基于平台条件性地导入 web_ffidart:ffi,然后重新导出。稍后我们将扩展此代理文件。创建 lib/src/proxy_ffi.dart 文件:

export 'package:web_ffi/web_ffi.dart' if (dart.library.ffi) 'dart:ffi';

2. 编写正常的绑定代码

绑定代码用于将C代码暴露给Dart,通常基于C库的 include 文件夹中的头文件。你可以手动编写代码,也可以使用工具如 <a href="https://github.com/dart-interop/ffi_tool/">ffi_tool</a><a href="https://pub.dev/packages/ffigen">ffigen</a> 来生成它。在绑定代码中,不要导入 dart:ffi 而是代理文件。如果你使用自动生成的绑定代码,必须手动更改导入语句!

这是我们要使用的绑定代码(由 ffi_tool 生成),保存在 lib/src/generated.dart

/// 包含来自 opus_defines.h 的 opus_libinfo 组的 opus 方法和结构体。
///
/// 自动生成的文件。请勿修改。

library opus_libinfo;

import 'proxy_ffi.dart' as ffi;

typedef _opus_get_version_string_C = ffi.Pointer&lt;ffi.Uint8&gt; Function();
typedef _opus_get_version_string_Dart = ffi.Pointer&lt;ffi.Uint8&gt; Function();

class FunctionsAndGlobals {
  FunctionsAndGlobals(ffi.DynamicLibrary _dynamicLibrary)
      : _opus_get_version_string = _dynamicLibrary.lookupFunction&lt;
            _opus_get_version_string_C, _opus_get_version_string_Dart&gt;(
          'opus_get_version_string',
        );

  /// 获取 libopus 版本字符串。
  ///
  /// 应用程序可以在版本字符串中查找子串 "-fixed" 以确定在运行时是否具有定点或浮点构建。
  ///
  /// @returns 版本字符串
  ffi.Pointer&lt;ffi.Uint8&gt; opus_get_version_string() {
    return _opus_get_version_string();
  }

  final _opus_get_version_string_Dart _opus_get_version_string;
}

3. 编译C代码以供Web使用

我们使用 <a href="https://emscripten.org/">emscripten</a> 将我们的C代码编译为WebAssembly,并生成所需的胶水JavaScript。重要的是传递 <code>-s MODULARIZE=1</code> 指令给 <code>emcc</code>,并且使用 <code>-s EXPORT_NAME=libopus</code> 为模块命名。在下面的内容中,我们假设模块名为 <code>libopus</code>,所以每次读到 <code>libopus</code> 时,请将其替换为你自己的模块名!此外,必须告诉 <code>emcc</code> 导出你想要绑定的所有C符号。你可以手动明确指定每个要导出的符号(参见emscripten文档了解如何操作),或者传递 <code>-s MAIN_MODULE=1</code> 指令来导出所有符号。必须确保 <code>malloc</code><code>free</code> 函数被导出(<code>-s MAIN_MODULE=1</code> 应该自动处理)。

为了方便的构建过程,我们使用 <a href="https://www.docker.com/">Docker</a>。这是我们的Dockerfile:

# 当前版本: 2.0.21
FROM emscripten/emsdk

RUN git clone --branch v1.3.1 https://github.com/xiph/opus.git
WORKDIR ./opus

RUN apt-get update \
    && DEBIAN_FRONTENTD="noninteractive" apt-get install -y --no-install-recommends \
    autoconf \
    libtool \
    automake

ENV CFLAGS='-O3 -fPIC'
ENV CPPFLAGS='-O3 -fPIC'
RUN ./autogen.sh \
    && emconfigure ./configure \
        --disable-intrinsics \
        --disable-rtcd \
        --disable-extra-programs \
        --disable-doc \
        --enable-static \
        --disable-stack-protector \
        --with-pic=ON \
    && emmake make
RUN mkdir emc_out \
    && emcc -O3 -s MAIN_MODULE=1 -s EXPORT_NAME=libopus -s MODULARIZE=1 ./.libs/libopus.a -o ./emc_out/libopus.js
WORKDIR ./emc_out

完成后,我们成功编译了 opus C库,并获得了 <code>libopus.js</code><code>libopus.wasm</code>

4. 初始化一切

现在是时候初始化一切了。我们的目标是获得一个 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi/DynamicLibrary-class.html">DynamicLibrary</a>。由于该API对于 <code>dart:ffi</code><code>web_ffi</code> 不同,我们需要编写两个文件,这两个文件将根据平台条件性地导入。

4.1 非Web平台

这是常规部分。创建 lib/src/init_ffi.dart 文件:

// 注意,在此文件中,我们导入的是 dart:ffi 而不是 proxy_ffi.dart
import 'dart:ffi';

// 对于 dart:ffi 平台,这可以是一个空操作(空函数)
Future&lt;void&gt; initFfi() async {
// 如果你只想支持web,取消以下异常
// throw new UnsupportedError('This package is only usable on the web!');
}

DynamicLibrary openOpus() {
// 如果你只想支持web,取消以下异常
// throw new UnsupportedError('This package is only usable on the web!');
  return new DynamicLibrary.open('libopus.so');
}

4.2 Web平台

在我们真正初始化 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/Module-class.html">Module</a> 之前,我们需要将 <code>libopus.wasm</code><code>libopus.js</code> 包含在项目中。有几种方法可以实现这一点。首先我们将讨论理论背景。然后你可以决定是否想遵循我们的示例来包含这些文件,或者知道一种更适合你项目的方案。

web_ffi 将绑定到你的web运行时,访问全局JavaScript对象并期望找到一个名为 <code>libopus</code> 的属性,该属性持有函数(称为 <code>module-function</code>),该函数接受一个参数(我们称之为 <code>arg0</code>)。通常,所有这些已经在 <code>libopus.js</code> 中写好了,所以我们需要包含该文件。如果该 <code>module-function</code> 之后从dart内部调用,它将尝试实例化实际的WebAssembly实例。由于我们使用 emscripten 编译,<code>module-function</code> 遵循标准的 emscripten 方法来获取 <code>libopus.wasm</code>。这意味着它将首先检查 <code>arg0['wasmBinary']</code> 是否包含 <code>libopus.wasm</code> 的字节,如果是,就使用它们。如果没有,它将尝试通过http(s)从同一个站点获取 <code>libopus.wasm</code>

4.2.1 使用Flutter

我们不想修改flutter构建后的文件,所以我们将在flutter应用中包含所需的一切。对于 <code>libopus.js</code><code>libopus.wasm</code>,我们将它们作为flutter资源包含进来,以便稍后从资源注入JavaScript到运行时,我们使用 <a href="https://pub.dev/packages/inject_js">inject_js</a> 插件。最后,我们将使用 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/EmscriptenModule/compile.html">EmscriptenModule.compile()</a> 函数,该函数也从资源加载字节,使用 <code>arg0['wasmBinary']</code> 方法。因此我们需要更新我们的 <code>pubspec.yaml</code>

name: web_ffi_example_flutter
publish_to: 'none'

version: 1.0.0+1

environment:
  sdk: "&gt;=2.12.0 &lt;3.0.0"

dependencies:
  flutter:
    sdk: flutter
  inject_js: ^2.0.0
  web_ffi:
    path: ../..

flutter:
  assets:
    - assets/libopus.js
    - assets/libopus.wasm

运行 <code>flutter packages get</code> 后,我们可以编写我们的初始化文件 lib/src/init_web.dart

import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:inject_js/inject_js.dart' as Js;
// 注意,在此文件中,我们导入的是 web_ffi 而不是 proxy_ffi.dart
import 'package:web_ffi/web_ffi.dart';
// 另外
import 'package:web_ffi/web_ffi_modules.dart';

// 如果你使用包中的资源而不是主应用中的资源,
// _basePath 会有所不同: 'packages/<package_name>/assets'
const String _basePath = 'assets';

Module? _module;

Future&lt;void&gt; initFfi() async {
  // 只有在没有模块时才初始化
  if (_module == null) {
    Memory.init();

    // 如果你的生成代码中包含扩展了 Opaque 的内容,
    // 你必须在这里注册它
    // registerOpaqueType&lt;MyOpaque&gt;();

    // 将JavaScript注入到页面中
    await Js.importLibrary('$_basePath/libopus.js');

    // 从资源加载WebAssembly二进制文件
    String path = '$_basePath/libopus.wasm';
    Uint8List wasmBinaries = (await rootBundle.load(path)).buffer.asUint8List();

    // 在加载完WebAssembly二进制文件并注入JavaScript代码后,
    // 我们获得一个模块
    _module = await EmscriptenModule.compile(wasmBinaries, 'libopus');
  }
}

DynamicLibrary openOpus() {
  Module? m = _module;
  if (m != null) {
    return new DynamicLibrary.fromModule(m);
  } else {
    throw new StateError('You can not open opus before calling initFfi()!');
  }
}
4.2.2 不使用Flutter

通常,如果不使用flutter,我们会使用 <code>dart2js</code> 输出一个JavaScript文件(我们称之为 <code>main.dart.js</code>),然后通过 <code>&lt;script&gt;</code> 标签将其包含在一个网站中。我们将简单地将 <code>libopus.js</code><code>libopus.wasm</code> 放在 <code>main.dart.js</code> 的同一文件夹中,并添加一个额外的 <code>&lt;script&gt;</code> 标签指向 <code>libopus.js</code>。我们的示例网站位于 <code>web</code> 文件夹中,所以我们创建 <code>web/index.html</code>

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>web_ffi 测试页面</title>
        <script defer src="main.dart.js"></script>
        <script src="libopus.js"></script>
    </head>
    <body>
    </body>
</html>

然后我们将使用 <a href="https://pub.dev/documentation/web_ffi/latest/web_ffi_modules/EmscriptenModule/process.html">EmscriptenModule.process()</a> 函数,该函数内部将使用没有 <code>wasmBinary</code> 设置的 <code>arg</code> 打开 <code>module-function</code>,因此 <code>emscripten</code> 逻辑将自动获取 <code>libopus.wasm</code>

这是我们的 <code>lib/src/init_web.dart</code>

// 注意,在此文件中,我们导入的是 web_ffi 而不是 proxy_ffi.dart
import 'package:web_ffi/web_ffi.dart';
// 另外
import 'package:web_ffi/web_ffi_modules.dart';

Module? _module;

Future&lt;void&gt; initFfi() async {
  // 只有在没有模块时才初始化
  if (_module == null) {
    Memory.init();

    // 如果你的生成代码中包含扩展了 Opaque 的内容,
    // 你必须在这里注册它
    // registerOpaqueType&lt;MyOpaque&gt;();

    // 因为我们已经通过&lt;script&gt;标签将libopus.js添加到HTML中,
    // 我们使用process函数
    _module = await EmscriptenModule.process('libopus');
  }
}

DynamicLibrary openOpus() {
  Module? m = _module;
  if (m != null) {
    return new DynamicLibrary.fromModule(m);
  } else {
    throw new StateError('You can not open opus before calling initFfi()!');
  }
}

4.3 更新代理

我们现在更新代理以导出正确的初始化文件。更改 <code>lib/src/proxy_ffi.dart</code> 如下所示:

export 'package:web_ffi/web_ffi.dart' if (dart.library.ffi) 'dart:ffi';
export 'init_web.dart' if (dart.library.ffi) 'init_ffi.dart';

5. 编写应用程序代码

现在我们编写应用程序代码。一般模式是导入 <code>proxy_ffi.dart</code>,并等待其 <code>initFfi()</code>。然后使用 <code>openOpus()</code> 获取 <code>DynamicLibrary</code>,并使用此 <code>DynamicLibrary</code> 实例化我们的绑定代码。最后使用绑定代码调用WebAssembly。再次说明,flutter和非flutter的代码略有不同。

5.0 解析C指针中的字符串

在C中,字符串通常表示为以0字节终止的一系列字节。我们要调用的函数给我们一个指向该序列第一个元素的指针。为了将其转换为Dart中可用的形式,我们将取该指针,通过搜索下一个0字节找到字符串长度,并使用 <code>dart:convert</code> 将其转换为Dart。

<code>lib/src/c_strings.dart</code> 中:

import 'dart:convert';
import 'proxy_ffi.dart';

String fromCString(Pointer&lt;Uint8&gt; cString) {
  int len = 0;
  while (cString[len] != 0) {
    len++;
  }
  return len &gt; 0 ? ascii.decode(cString.asTypedList(len)) : '';
}

/// 在使用完C字符串后,记得使用相同的分配器释放它!
Pointer&lt;Uint8&gt; toCString(String dartString, Allocator allocator) {
  List&lt;int&gt; bytes = ascii.encode(dartString);
  Pointer&lt;Uint8&gt; cString = allocator.allocate&lt;Uint8&gt;(bytes.length);
  cString.asTypedList(bytes.length).setAll(0, bytes);
  return cString;
}

5.1 使用Flutter

如果我们使用flutter,编辑 <code>lib/main.dart</code> 使其看起来像这样:

import 'package:flutter/material.dart';
import 'src/proxy_ffi.dart';
import 'src/c_strings.dart';
import 'src/generated.dart';

Future&lt;void&gt; main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initFfi();
  DynamicLibrary dynLib = openOpus();
  FunctionsAndGlobals opusLibinfo = FunctionsAndGlobals(dynLib);
  String version = fromCString(opusLibinfo.opus_get_version_string());
  runApp(MyApp(version));
}

class MyApp extends StatelessWidget {
  final String _opusVersion;

  const MyApp(this._opusVersion);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'web_ffi Demo',
      home: Scaffold(
          appBar: AppBar(
            title: Text('web_ffi Demo'),
            centerTitle: true,
          ),
          body: Container(
            alignment: Alignment.center,
            child: Text(_opusVersion),
          )),
    );
  }
}

5.2 不使用Flutter

首先,为了正确导出 <code>lib</code> 目录中的文件,我们创建 <code>lib/example_no_flutter.dart</code>

library example_no_flutter;

export 'src/proxy_ffi.dart';
export 'src/generated.dart';
export 'src/c_strings.dart';

如果我们不使用flutter,我们的主文件是 <code>bin/main.dart</code>,应该看起来像这样:

import 'package:web_ffi_example_no_flutter/example_no_flutter.dart';

Future&lt;void&gt; main() async {
  await initFfi();
  DynamicLibrary opus = openOpus();
  FunctionsAndGlobals opusLibinfo = new FunctionsAndGlobals(opus);
  Pointer&lt;Uint8&gt; cString = opusLibinfo.opus_get_version_string();
  print(fromCString(cString));
}

6. 运行应用程序

一切都准备好了,现在是时候运行应用程序了。

6.1 使用Flutter

使用flutter很简单,只需运行:

flutter run -d chrome

Chrome将会打开。 在那里,我们应该看到一个文本字段,显示 <code>libopus 1.3.1</code>

6.2 不使用Flutter

我们在第4.2.2节中已经设置了大部分需要的文件。唯一缺少的是 <code>main.dart.js</code>,所以我们使用 <code>dart2js</code> 创建它:

dart2js ./bin/main.dart -o ./web/main.dart.js 

接下来,我们需要通过http提供 <code>web</code> 目录。为此我们使用 <a href="https://pub.dev/packages/dhttpd">dhttpd</a>,一个用dart编写的简单web服务器。要安装它,请运行:

pub global activate dhttpd

然后进入 <code>web</code> 目录:

cd web

然后从此目录运行 <code>dhttpd</code>

pub global run dhttpd -p 8080

更多关于Flutter Web技术集成插件web_ffi_fork的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


当然,关于在Flutter Web项目中集成web_ffi_fork插件,以下是一个简单的代码案例来展示如何使用这个插件。web_ffi_fork是一个允许在Flutter Web应用中使用FFI(外部函数接口)的插件,从而能够调用原生WebAssembly或其他原生代码。

首先,确保你的Flutter环境已经设置好,并且你的项目已经创建。接下来,按照以下步骤操作:

  1. 添加依赖: 在pubspec.yaml文件中添加web_ffi_fork依赖。

    dependencies:
      flutter:
        sdk: flutter
      web_ffi_fork: ^x.y.z  # 替换为最新版本号
    

    然后运行flutter pub get来安装依赖。

  2. 配置index.html: 由于web_ffi_fork需要使用WebAssembly,你可能需要在web/index.html中添加一些配置来加载WASM文件。不过,这通常在你使用具体的FFI绑定库时会有更详细的指导。

  3. 使用FFI加载和调用WebAssembly模块: 下面是一个基本的示例,展示如何使用web_ffi_fork来加载一个假设的WebAssembly模块,并调用其中的一个函数。

    import 'dart:ffi';
    import 'dart:typed_data';
    import 'package:ffi/ffi.dart';
    import 'package:web_ffi_fork/web_ffi_fork.dart';
    
    typedef NativeFunc = Int32 Function(Int32);
    typedef DartFunc = int Function(int);
    
    void main() async {
      // 假设你有一个名为 'example.wasm' 的 WebAssembly 文件
      final wasmData = await http.readBytes('path/to/example.wasm'); // 注意:这里需要使用适当的路径或URL
      final wasmModule = WebAssembly.instantiate(wasmData);
    
      // 等待 WebAssembly 模块实例化完成
      final instance = await wasmModule;
    
      // 获取 WebAssembly 导出的函数
      final funcPointer = instance.exports['yourExportedFunctionName'] as IntPtr;
    
      // 创建一个 Dart 函数指针,指向 WebAssembly 函数
      final nativeFunc = Pointer<NativeFunction<NativeFunc>>.fromAddress(funcPointer.address);
      final ffiFunc = nativeFunc.asFunction<DartFunc>();
    
      // 调用 WebAssembly 函数
      final result = ffiFunc(42);
      print('Result from WebAssembly function: $result');
    }
    

    注意

    • 上述代码中的yourExportedFunctionName应该替换为你实际WebAssembly模块中导出的函数名。
    • http.readBytes用于读取WASM文件,这里你需要确保有正确的方式来访问你的WASM文件,可能是在本地开发环境中,也可能是通过网络URL。
    • WebAssembly.instantiateweb_ffi_fork提供的方法,用于实例化WebAssembly模块。
  4. 处理可能的错误: 在实际应用中,你应该添加适当的错误处理逻辑来处理加载WASM文件或调用函数时可能出现的错误。

这个示例提供了一个基本的框架,展示了如何在Flutter Web应用中使用web_ffi_fork来加载和调用WebAssembly模块。根据你的具体需求,你可能需要调整这个框架来适应你的WebAssembly模块和导出函数。

回到顶部