前言

文章的图片链接都是在github上,可能需求...你懂得;本文含有很多关键步骤装备图片,强烈建议在合适环境下阅览

Flutter直接调用C层还是蛮有魅力,想想你操练C++,然后直接能用flutter在上层展示出作用,是不是就有很多练手的机会了,逻辑反手就用C++,Rust去写,给后边的接盘侠留下一座绚丽的克苏鲁神山,供其仰视

上面仅仅开个玩笑,现在flutter ffi的交互,主要是为了和底层交互的一致,还能直接运用到很多宝藏相同的底层库

现在ffi的同步调用还是比较能够,异步交互有办法去处理,可是运用起来比较费事

  • 有爱好的能够检查下面异步音讯通讯模块中贴的issue

Flutter和Rust的交互

  • flutter_rust_bridge库给了一个很不错的处理方案
  • 主要是他能很轻松的完成异步交互!

本文是循序渐进式,比较全面的介绍了flutter的ffi运用,ffigen运用,最终才是rust交互介绍;假如对ffi和ffigen不太关怀,也可直接阅览rust交互内容

FFI交互方法

装备

Android

  • 需求先装备ndk
# mac
ndk.dir=/Users/***/Develop/SDK/android_sdk/ndk/21.3.6528147
# windows
ndk.dir=F:\\SDK\\AndroidSDK\\ndk\\21.3.6528147

  • 装置下CMake

Flutter和Rust如何优雅的交互

  • 需求在Android的build.gradle里装备下cmake途径
android {
    ...
    //装备CMakeList途径
    externalNativeBuild {
        cmake {
            path "../lib/native/CMakeLists.txt"
        }
    }
}

Flutter和Rust如何优雅的交互

  • 由于Windows和Linux都需求用到CMakeLists.txt,先来看下Android的装备
    • Android的比较简单,装备下需求编译的c文件就行了
    • 一个个增加文件的方法太费事了,这边直接用native_batch批量增加文件
    • android会指定给界说的项目名上加上libset(PROJECT_NAME "native_fun")生成的名称应该为libnative_fun.so
# cmake_minimum_required 表明支撑的 cmake 最小版别
cmake_minimum_required(VERSION 3.4.1)
# 项目名称
set(PROJECT_NAME "native_fun")
# 批量增加c文件
# add_library 关键字表明构建链接库,参数1是链接包名称; 参数2'SHARED'表明构建动态链接库; 参数2是源文件列表
file(GLOB_RECURSE native_batch ../../ios/Classes/native/*)
add_library(${PROJECT_NAME} SHARED ${native_batch})

能够发现file(GLOB_RECURSE native_batch ../../ios/Classes/native/*)这边途径设置在iOS的Classes文件下,这边是为了便利一致编译native文件夹下的一切c文件,macOS和iOS需求放在Classes下,能够直接编译

可是macOS和iOS无法指定编译超越父节点方位,有必要放在Classes子文件夹下,超越这个节点就无法编译

所以这边iOS和macOS有必要要维护俩份相同c文件(建个文件夹吧,便利直接拷贝过去);Android,Windows,Linux能够指定到这俩个中的其中之一(建议指定iOS的Classes,防止一些灵异Bug)

  • 作用

Flutter和Rust如何优雅的交互

iOS

  • iOS能够直接编译C文件,需求放在Classes文件夹下

Flutter和Rust如何优雅的交互

  • 作用

Flutter和Rust如何优雅的交互

macOS

  • macOS也能够直接编译C文件,需求放在Classes文件夹下

Flutter和Rust如何优雅的交互

  • 作用

Flutter和Rust如何优雅的交互

Windows

  • windows下的CMakeLists.txt里边指定了lib/native下面的一致CMakeLists.txt装备
# cmake_minimum_required 表明支撑的 cmake 最小版别
cmake_minimum_required(VERSION 3.4.1)
# 项目名称
set(PROJECT_NAME "libnative_fun")
# 批量增加cpp文件
# add_library 关键字表明构建链接库,参数1是链接包名称; 参数2'SHARED'表明构建动态链接库; 参数2是源文件列表
file(GLOB_RECURSE native_batch ../../ios/Classes/native/*)
add_library(${PROJECT_NAME} SHARED ${native_batch})
# Windows 需求把dll拷贝到bin目录
# 动态库的输出目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<$<CONFIG:DEBUG>:Debug>$<$<CONFIG:RELEASE>:Release>")
# 装置动态库的方针目录
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
# 装置动态库,到履行目录
install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${PROJECT_NAME}.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime)

Flutter和Rust如何优雅的交互

  • 这边能够将Android和Windows的装备一致下,加下判断即可
# cmake_minimum_required 表明支撑的 cmake 最小版别
cmake_minimum_required(VERSION 3.4.1)
# 项目名称
if (WIN32)
    set(PROJECT_NAME "libnative_fun")
else()
    set(PROJECT_NAME "native_fun")
endif()
# 批量增加c文件
# add_library 关键字表明构建链接库,参数1是链接包名称; 参数2'SHARED'表明构建动态链接库; 参数2是源文件列表
file(GLOB_RECURSE native_batch ../../ios/Classes/native/*)
add_library(${PROJECT_NAME} SHARED ${native_batch})
# Windows 需求把dll拷贝到bin目录
if (WIN32)
    # 动态库的输出目录
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<$<CONFIG:DEBUG>:Debug>$<$<CONFIG:RELEASE>:Release>")
    # 装置动态库的方针目录
    set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
    # 装置动态库,到履行目录
    install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${PROJECT_NAME}.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime)
endif()
  • 阐明下,Windows这边有必要将生成的dll拷贝到bin目录下,才干调用
    • 所以cmake里边,最终那段Windows的特有代码是有必要要写的

Flutter和Rust如何优雅的交互

  • 作用

Flutter和Rust如何优雅的交互

交互

  • 通用加载:NativeFFI.dynamicLibrary
class NativeFFI {
  NativeFFI._();
  static DynamicLibrary? _dyLib;
  static DynamicLibrary get dynamicLibrary {
    if (_dyLib != null) return _dyLib!;
    if (Platform.isMacOS || Platform.isIOS) {
      _dyLib = DynamicLibrary.process();
    } else if (Platform.isAndroid) {
      _dyLib = DynamicLibrary.open('libnative_fun.so');
    } else if (Platform.isWindows) {
      _dyLib = DynamicLibrary.open('libnative_fun.dll');
    } else {
      throw Exception('DynamicLibrary初始化失利');
    }
    return _dyLib!;
  }
}

Flutter同步调用Native

  • dart
/// 俩数相加
int ffiAddSyncInvoke(int a, int b) {
  final int Function(int x, int y) nativeAdd = NativeFFI.dynamicLibrary
      .lookup<NativeFunction<Int32 Function(Int32, Int32)>>("twoNumAdd")
      .asFunction();
  return nativeAdd(a, b);
}
  • native
#include <stdint.h>
#ifdef WIN32
#define DART_API extern "C" __declspec(dllexport)
#else
#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))
#endif
DART_API int32_t twoNumAdd(int32_t x, int32_t y){
    return x + y;
}
  • 作用

Flutter和Rust如何优雅的交互

Native同步触发Flutter回调

  • dart
/// 传递的回调
typedef _NativeCallback = Int32 Function(Int32 num);
/// Native办法
typedef _NativeSyncCallback = Void Function(
  Pointer<NativeFunction<_NativeCallback>> callback,
);
/// Dart结束回调: Void和void不同,所以要区分隔
typedef _DartSyncCallback = void Function(
  Pointer<NativeFunction<_NativeCallback>> callback,
);
/// 有必要运用顶层办法或许静态办法
/// macos端能够打印出native层日志, 移动端只能打印dart日志
int _syncCallback(int num) {
  print('--------');
  return num;
}
/// 在native层打印回调传入的值
void ffiPrintSyncCallback() {
  final _DartSyncCallback dartSyncCallback = NativeFFI.dynamicLibrary
      .lookup<NativeFunction<_NativeSyncCallback>>("nativeSyncCallback")
      .asFunction();
  // 包装传递的回调
  var syncFun = Pointer.fromFunction<_NativeCallback>(_syncCallback, 0);
  dartSyncCallback(syncFun);
}
  • native
#include <stdint.h>
#include <iostream>
#ifdef WIN32
#define DART_API extern "C" __declspec(dllexport)
#else
#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))
#endif
using namespace std;
// 界说传递的回调类型
typedef int32_t (*NativeCallback)(int32_t n);
DART_API void nativeSyncCallback(NativeCallback callback) {
    // 打印
    std::cout << "native log callback(666) = " << callback(666) << std::endl;
}
  • 作用

Flutter和Rust如何优雅的交互

异步音讯通讯

阐明

异步交互的写法有点复杂,能够检查下面的谈论

  • github.com/dart-lang/s…
  • stackoverflow.com/questions/6…

异步通讯需求导入额定c文件用作通讯支撑,可是假如你的iOS项目是swift项目,无法编译这些额定c文件

  • 这些c文件我是封装在插件里,没想到办法怎样树立桥接
  • 假如是OC项目,就能够直接编译

现在来看

  • Android和iOS能够编译额定的音讯通讯的c文件
  • windows和macos试了,都无法编译,麻了

运用

  • dart
import 'dart:async';
import 'dart:ffi';
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:flutter_ffi_toolkit/src/native_ffi.dart';
ReceivePort? _receivePort;
StreamSubscription? _subscription;
void _ensureNativeInitialized() {
  if (_receivePort == null) {
    WidgetsFlutterBinding.ensureInitialized();
    final initializeApi = NativeFFI.dynamicLibrary.lookupFunction<
        IntPtr Function(Pointer<Void>),
        int Function(Pointer<Void>)>("InitDartApiDL");
    if (initializeApi(NativeApi.initializeApiDLData) != 0) {
      throw "Failed to initialize Dart API";
    }
    _receivePort = ReceivePort();
    _subscription = _receivePort!.listen(_handleNativeMessage);
    final registerSendPort = NativeFFI.dynamicLibrary.lookupFunction<
        Void Function(Int64 sendPort),
        void Function(int sendPort)>('RegisterSendPort');
    registerSendPort(_receivePort!.sendPort.nativePort);
  }
}
void _handleNativeMessage(dynamic address) {
  print('---------native端通讯,地址: $address');
  Pointer<Int32> point = Pointer<Int32>.fromAddress(address);
  print('---------native端通讯,指针: $point');
  dynamic data = point.cast();
  print('---------native端通讯,cast: $data');
}
void ffiAsyncMessage(int a) {
  _ensureNativeInitialized();
  final void Function(int x) asyncMessage = NativeFFI.dynamicLibrary
      .lookup<NativeFunction<Void Function(Int32)>>("NativeAsyncMessage")
      .asFunction();
  asyncMessage(a);
}
void dispose() {
  // TODO _unregisterReceivePort(_receivePort.sendPort.nativePort);
  _subscription?.cancel();
  _receivePort?.close();
}
  • native
// C
#include <stdio.h>
// Unix
#include <unistd.h>
#include <pthread.h>
#include "dart_api/dart_api.h"
#include "dart_api/dart_native_api.h"
#include "dart_api/dart_api_dl.h"
// Initialize `dart_api_dl.h`
DART_EXPORT intptr_t InitDartApiDL(void* data) {
  return Dart_InitializeApiDL(data);
}
Dart_Port send_port_;
DART_EXPORT void RegisterSendPort(Dart_Port send_port) {
  send_port_ = send_port;
}
void *thread_func(void *args) {
    printf("thread_func Running on (%p)\n", pthread_self());
    sleep(2 /* seconds */); // doing something
    Dart_CObject dart_object;
    dart_object.type = Dart_CObject_kInt64;
    dart_object.value.as_int64 = reinterpret_cast<intptr_t>(args);
    Dart_PostCObject_DL(send_port_, &dart_object);
    pthread_exit(args);
}
DART_EXPORT void NativeAsyncMessage(int32_t x) {
    printf("NativeAsyncCallback Running on (%p)\n", pthread_self());
    pthread_t message_thread;
    pthread_create(&message_thread, NULL, thread_func, (void *)&x);
}
  • 额定c文件:github.com/xdd666t/flu…

Flutter和Rust如何优雅的交互

  • 作用

Flutter和Rust如何优雅的交互

ffigen运用

手写这些ffi交互代码,也是件比较费事的事,而且每个办法都要写对应的类型转化和相应的硬编码办法名,假如c的某个办法改动参数和办法名,再回去改对应的dart代码,无疑是一件蛋痛的事

flutter供给了一个主动生成ffi交互的代码,浅显的说:主动将c代码生成为对应dart的代码

装备

  • ubuntu/linux

    • 装置 libclangdev: sudo apt-get install libclang-dev
  • Windows

    • 装置 Visual Studio with C++ development support
    • 装置 LLVM: winget install -e --id LLVM.LLVM
  • MacOS

    • 装置 Xcode

    • 装置 LLVM: brew install llvm

  • 引进ffigen

    • pub:pub.dev/packages/ff…
dependencies:
  ffigen: ^7.2.0
ffigen:
  # 输出生成的文件途径
  output: 'lib/src/ffigen/two_num_add.dart'
  # 输出的类名
  name: NativeLibrary
  headers:
    # 装备需求生成的文件
    entry-points:
      - 'ios/Classes/native/ffigen/add.cpp'
    # 保证只转化two_num_add.cpp文件,不转化其包含的库文件,建议加上
    include-directives:
      - 'ios/Classes/native/ffigen/add.cpp'

生成文件

  • 需求留意:生成的文件方位,需求和指定文件的编译方位保持一致,这样才干编译这些c文件

Flutter和Rust如何优雅的交互

  • ffigen生成指令
dart run ffigen
  • add.cpp
    • 运用指令生成对应dart文件的时分,办法名前不能加咱们界说的DART_API,否则无法生成对应dart文件
    • 编译的时分有必要要加上DART_API,否则无法编译该办法
    • 有点无语,有知道能一致处理cpp文件办法的,还请在谈论区告知呀
#include <stdint.h>
#ifdef WIN32
#define DART_API extern "C" __declspec(dllexport)
#else
#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))
#endif
// DART_API int32_t twoNumAddGen(int32_t x, int32_t y){
//     return x + y;
// }
int32_t twoNumAddGen(int32_t x, int32_t y){
    return x + y;
}
  • 生成的dart文件
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
class NativeLibrary {
  /// Holds the symbol lookup function.
  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
      _lookup;
  /// The symbols are looked up in [dynamicLibrary].
  NativeLibrary(ffi.DynamicLibrary dynamicLibrary)
      : _lookup = dynamicLibrary.lookup;
  /// The symbols are looked up with [lookup].
  NativeLibrary.fromLookup(
      ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
          lookup)
      : _lookup = lookup;
  int twoNumAddGen(
    int x,
    int y,
  ) {
    return _twoNumAddGen(
      x,
      y,
    );
  }
  late final _twoNumAddGenPtr =
      _lookup<ffi.NativeFunction<ffi.Int32 Function(ffi.Int32, ffi.Int32)>>(
          'twoNumAddGen');
  late final _twoNumAddGen =
      _twoNumAddGenPtr.asFunction<int Function(int, int)>();
}

Flutter和Rust如何优雅的交互

运用

  • 通用加载:NativeFFI.dynamicLibrary
class NativeFFI {
  NativeFFI._();
  static DynamicLibrary? _dyLib;
  static DynamicLibrary get dynamicLibrary {
    if (_dyLib != null) return _dyLib!;
    if (Platform.isMacOS || Platform.isIOS) {
      _dyLib = DynamicLibrary.process();
    } else if (Platform.isAndroid) {
      _dyLib = DynamicLibrary.open('libnative_fun.so');
    } else if (Platform.isWindows) {
      _dyLib = DynamicLibrary.open('libnative_fun.dll');
    } else {
      throw Exception('DynamicLibrary初始化失利');
    }
    return _dyLib!;
  }
}
  • 运用
NativeLibrary(NativeFFI.dynamicLibrary).twoNumAddGen(a, b);
  • 作用

Flutter和Rust如何优雅的交互

rust 交互

运用flutter_rust_bridge:flutter_rust_bridge

下面全平台的装备,我成功编译运转后写的一份具体指南(踩了一堆坑),大家务必仔细按照步骤装备~

大家也能够参阅官方文档,不过我觉得写的更加人性化,hhhhhh…

  • 原版:cjycode.com/flutter_rus…
  • 中文版:trdthg.github.io/flutter_rus…

准备

创立Rust项目

  • Rust装置:rustup.rs/
  • 创立项目,请挑选library

Flutter和Rust如何优雅的交互

  • Cargo.toml 需求引进三个库:[package]和[lib]中的name参数,请保持一致,此处示例是name = "rust_ffi"
    • [lib]:crate-type =[“lib”, “staticlib”, “cdylib”]
    • [build-dependencies]:flutter_rust_bridge_codegen
    • [dependencies]:flutter_rust_bridge
    • 最新版别检查:crates.io/
[package]
name = "rust_ffi"
version = "0.1.0"
edition = "2021"
[lib]
name = "rust_ffi"
crate-type = ["staticlib", "cdylib"]
[build-dependencies]
flutter_rust_bridge_codegen = "=1.51.0"
[dependencies]
flutter_rust_bridge = "=1.51.0"
flutter_rust_bridge_macros = "=1.51.0"
  • 写rust代码需求留意下,不要在lib.rs中写代码,否则生成文件无法获取导包

Flutter和Rust如何优雅的交互

Flutter项目

  • flutter项目正常创立就行了

Flutter和Rust如何优雅的交互

  • flutter的pubspec.yaml中需求增加这些库
dependencies:
  # https://pub.dev/packages/flutter_rust_bridge
  flutter_rust_bridge: 1.51.0
  ffi: ^2.0.1
dev_dependencies:
  ffigen: ^7.0.0

指令

  • 需求先装置下代码生成东西
# 有必要
cargo install flutter_rust_bridge_codegen
# iOS和macOS 有必要需求
cargo install cargo-xcode
  • 装置LLVM

    • ubuntu/linux

      • 装置 libclangdev: sudo apt-get install libclang-dev
    • Windows

      • 装置 Visual Studio with C++ development support
      • 装置 LLVM: winget install -e --id LLVM.LLVM
    • MacOS

      • 装置 Xcode

      • 装置 LLVM: brew install llvm

  • 生成指令

flutter_rust_bridge_codegen -r rust/src/api.rs -d lib/ffi/rust_ffi/rust_ffi.dart
  • 假如需求iOS和macOS,用下面的指令,阐明请参照:装备 —> iOS / macOS
flutter_rust_bridge_codegen -r rust/src/api.rs -d lib/ffi/rust_ffi/rust_ffi.dart -c ios/Runner/bridge_generated.h -c macos/Runner/bridge_generated.h
  • 请留意
    • 假如你在flutter侧晋级了flutter_rust_bridge版别
    • rust的Cargo.toml也应该对flutter_rust_bridge_codegenflutter_rust_bridge晋级对应版别
    • 晋级完版别后需求重新跑下该指令
# 主动装置最新版别
cargo install flutter_rust_bridge_codegen
# 指定版别
cargo install flutter_rust_bridge_codegen --version 1.51.0 --force

装备

Android

  • 有必要要装置cargo-ndk :它能够将代码编译到合适的 JNI 而不需求额定的装备
cargo install cargo-ndk
  • 增加cargo的android编译东西,在指令行履行下下述指令
rustup target add aarch64-linux-android
rustup target add armv7-linux-androideabi
rustup target add x86_64-linux-android
rustup target add i686-linux-android
  • NDK:请运用NDK 22或更早的版别,NDK下载请参阅下图

Flutter和Rust如何优雅的交互

  • 需求在gradle.properties装备下
# mac
ANDROID_NDK=/Users/***/Develop/SDK/android_sdk/ndk/21.3.6528147
# windows
ANDROID_NDK=F:\\SDK\\AndroidSDK\\ndk\\21.3.6528147

Flutter和Rust如何优雅的交互

  • android/app/build.gradle 的最终增加下面几行
    • ANDROID_NDK 便是在上面装备的变量
    • “../../rust”:此处请装备自己rust项目文件夹命名
[
    new Tuple2('Debug', ''),
    new Tuple2('Profile', '--release'),
    new Tuple2('Release', '--release')
].each {
    def taskPostfix = it.first
    def profileMode = it.second
    tasks.whenTaskAdded { task ->
        if (task.name == "javaPreCompile$taskPostfix") {
            task.dependsOn "cargoBuild$taskPostfix"
        }
    }
    tasks.register("cargoBuild$taskPostfix", Exec) {
        // Until https://github.com/bbqsrc/cargo-ndk/pull/13 is merged,
        // this workaround is necessary.
        def ndk_command = """cargo ndk \
            -t armeabi-v7a -t arm64-v8a -t x86_64 -t x86 \
            -o ../android/app/src/main/jniLibs build $profileMode"""
        workingDir "../../rust"
        environment "ANDROID_NDK_HOME", "$ANDROID_NDK"
        if (org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem.isWindows()) {
            commandLine 'cmd', '/C', ndk_command
        } else {
            commandLine 'sh', '-c', ndk_command
        }
    }
}

iOS

  • iOS 需求一些额定的交叉编译方针:
# 64 bit targets (真机 & 模拟器):
rustup target add aarch64-apple-ios x86_64-apple-ios
# New simulator target for Xcode 12 and later
rustup target add aarch64-apple-ios-sim
  • 需求先生成子项目
# 在rust项目下履行该指令
cargo xcode

增加一些绑定文件

  • 在 Xcode 中翻开 ios/Runner.xcodeproj, 接着把 $crate/$crate.xcodeproj 增加为子项目:File —> Add Files to “Runner”

Flutter和Rust如何优雅的交互

  • 挑选生成子项目,然后点击add

Flutter和Rust如何优雅的交互

  • 选中那个文件夹,就会生成在哪个文件下

Flutter和Rust如何优雅的交互

  • 点击 Runner 根项目,TARGETS —> Build Phases —> Target Dependencies :请增加 $crate-staticlib

Flutter和Rust如何优雅的交互

  • 打开 Link Binary With Libraries:增加 lib$crate_static.a

Flutter和Rust如何优雅的交互

  • 增加结束后

Flutter和Rust如何优雅的交互

绑定头文件

flutter_rust_bridge_codegen 会创立一个 C 头文件,里边列出了 Rust 库导出的一切符号,需求运用它,确保 Xcode 不会将符号去除。

在项目中需求增加 ios/Runner/bridge_generated.h (或许 macos/Runner/bridge_generated.h)

  • 履行下述生成指令,会生成对应头文件,主动放到ios和macos目录下;能够封装成脚本,每次跑脚本就行了
flutter_rust_bridge_codegen -r rust/src/api.rs -d lib/ffi/rust_ffi/rust_ffi.dart -c ios/Runner/bridge_generated.h -c macos/Runner/bridge_generated.h

Flutter和Rust如何优雅的交互

  • ios/Runner/Runner-Bridging-Header.h 中增加

    #import "GeneratedPluginRegistrant.h"
    +#import "bridge_generated.h"
    
  • ios/Runner/AppDelegate.swift 中增加

     override func application(
         _ application: UIApplication,
         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
     ) -> Bool {
    +    let dummy = dummy_method_to_enforce_bundling()
    +    print(dummy)
         GeneratedPluginRegistrant.register(with: self)
         return super.application(application, didFinishLaunchingWithOptions: launchOptions)
     }
    

macOS

阐明

macos上面指向有个很奇怪的情况,官方文档阐明的是需求链接$crate-cdylib$crate.dylib ;可是链接这个库,用xcode编译能够履行,可是运用android studio直接编译履行的时分会报错

  • 通过参阅该库:github.com/Desdaemon/f…

与iOS保持一致,链接 $crate-staticliblib$crate_static.a ,能够顺畅履行

下面装备,大家按需装备,我这边运用静态库能成功,链接动态库会失利

开端装备

  • 需求先生成子项目,假如在装备iOS的时分现已履行了该指令,就不需求再次履行了(当然,再次履行也没问题)
# 在rust项目下履行该指令
cargo xcode
  • 在 Xcode 中翻开 macos/Runner.xcodeproj, 接着把 $crate/$crate.xcodeproj 增加为子项目:File —> Add Files to “Runner”

Flutter和Rust如何优雅的交互

  • 点击 Runner 根项目,TARGETS —> Build Phases —> Target Dependencies :请增加 $crate-staticlib (或许 $crate-staticlib )

Flutter和Rust如何优雅的交互

  • 打开 Link Binary With Libraries: 增加 lib$crate_static.a (或许 $crate.dylib )

Flutter和Rust如何优雅的交互

  • 需求留意的是,假如运用了动态库,编译报找不到 $crate.dylib的时分
    • 能够在Link Binary With Libraries的时分,macOS增加的**.dylib要挑选Optional
    • 这个问题可能并不是必现

  • Flutter 在 MacOS 上默认不运用符号,咱们需求增加咱们自己的

    • Build Settings 标签页中

    • Objective-C Bridging Header 设置为: Runner/bridge_generated.h

Flutter和Rust如何优雅的交互

  • 还需求把bridge_generated.h文件加入macos项目

Flutter和Rust如何优雅的交互

Flutter和Rust如何优雅的交互

  • macos/Runner/AppDelegate.swift 中增加

    import Cocoa
    import FlutterMacOS
    @NSApplicationMain
    class AppDelegate: FlutterAppDelegate {
      override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
    +   dummy_method_to_enforce_bundling()
        return true
      }
    }
    

Windows / Linux

阐明

现在运用flutter_rust_bridge等库保持在1.51.0版别

  • 亲测在windows,android,ios,macos能够编译运转,linux(暂时没精力折腾)

flutter_rust_bridge更新到1.54.0版别

  • 该版别有个比较大的改动,生成代码改动也比较大,需求装置最新版flutter_rust_bridge_codegen去生成代码
  • 该版别在android,ios,macos能够编译运转,在windows上会报错
failed to run custom build command for `dart-sys v2.0.1`
Microsoft.CppCommon.targets(247,5): error MSB8066

猜想是作者新版别dart-sys v2.0.1这个库有问题,导致编译产物途径出了问题,报错了上面的错

现在本demo的版别号限制死在1.51.0版别;后边作者可能会处理该问题,需求运用新版别可自行测验

  • 在windows上装置编译生成库,需求装置指定版别
# 指定版别
cargo install flutter_rust_bridge_codegen --version 1.51.0 --force

重要阐明

由于windows上编译需求下载Corrosion,需求开启全局xx上网,否则可能在编译的时分,会存在无法下载Corrosion的报错

引荐东西运用sstap 1.0.9.7版别,这个版别内置全局规矩(可戴笠软件,不仅限浏览器),后边的版别该规矩被删了

rust.make

  • 需求先装置Corrosion ,可参阅:github.com/corrosion-r…
    • 需求装置cmake(挑选装置到环境变量的方法):cmake.org/download/
    • 然后履行下述指令
git clone https://github.com/corrosion-rs/corrosion.git
# Optionally, specify -DCMAKE_INSTALL_PREFIX=<target-install-path>. You can install Corrosion anyway
cmake -Scorrosion -Bbuild -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
# This next step may require sudo or admin privileges if you're installing to a system location,
# which is the default.
cmake --install build --config Release

windows和linux需求增加个rust.make文件:

  • rust.make里边标注的内容需求和Cargo.toml里name保持一致

Flutter和Rust如何优雅的交互

  • rust.make
# We include Corrosion inline here, but ideally in a project with
# many dependencies we would need to install Corrosion on the system.
# See instructions on https://github.com/AndrewGaspar/corrosion#cmake-install
# Once done, uncomment this line:
# find_package(Corrosion REQUIRED)
include(FetchContent)
FetchContent_Declare(
    Corrosion
    GIT_REPOSITORY https://github.com/AndrewGaspar/corrosion.git
    GIT_TAG origin/master # Optionally specify a version tag or branch here
)
FetchContent_MakeAvailable(Corrosion)
corrosion_import_crate(MANIFEST_PATH ../rust/Cargo.toml CRATES rust_ffi)
# Flutter-specific
set(CRATE_NAME "rust_ffi")
target_link_libraries(${BINARY_NAME} PRIVATE ${CRATE_NAME})
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${CRATE_NAME}-shared>)

调整

  • Windows:在windows/CMakeLists.txt增加rust.cmake文件
 # Generated plugin build rules, which manage building the plugins and adding
 # them to the application.
 include(flutter/generated_plugins.cmake)
+include(./rust.cmake)
 # === Installation ===
 # Support files are copied into place next to the executable, so that it can
  • Linux:在 Linux 上,你需求将 CMake 的最低版别升到 3.12,这是 Corrosion 的要求,rust.cmake 依赖 Corrosion。需求修改 linux/CMakeLists.txt 的这一行
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.12)
...
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
+include(./rust.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build

运用

  • 调用
class NativeFFI {
  NativeFFI._();
  static DynamicLibrary? _dyLib;
  static DynamicLibrary get dyLib {
    if (_dyLib != null) return _dyLib!;
    const base = 'rust_ffi';
    if (Platform.isIOS) {
      _dyLib = DynamicLibrary.process();
    } else if (Platform.isMacOS) {
      _dyLib = DynamicLibrary.executable();
    } else if (Platform.isAndroid) {
      _dyLib = DynamicLibrary.open('lib$base.so');
    } else if (Platform.isWindows) {
      _dyLib = DynamicLibrary.open('$base.dll');
    } else {
      throw Exception('DynamicLibrary初始化失利');
    }
    return _dyLib!;
  }
}
class NativeFun {
  static final _ffi = RustFfiImpl(NativeFFI.dyLib);
  static Future<int> add(int left, int right) async {
    int sum = await _ffi.add(left: left, right: right);
    return sum;
  }
}
  • 主动生成的类就不写了,便是上面运用的RustFfiImpl

Flutter和Rust如何优雅的交互

  • 运用
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(title: 'Flutter Demo', home: MyHomePage());
  }
}
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  void _incrementCounter() async {
    _counter = await NativeFun.add(_counter, 2);
    setState(() {});
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Rust_Bridge Demo')),
      body: Center(
        child: Text(
          'Count:  $_counter',
          style: Theme.of(context).textTheme.headline4,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
  • 作用

结语

对于rust这块,这些装备确实有点费事,可是装备完,后边就不用管了

痛苦一次就行了~.~

  • 项目地址:github.com/xdd666t/flu…