环境
Python 3.11.0
rustc 1.68.0 (2c8cc3432 2023-03-06)
Mac
调用流程
c++或rust
anoidrd生成so文件, ios生成.a文件
通过ffi间接调用
flutter 调用.cpp
native_lib.cpp
#include <stdint.h> #include <cstring> extern "C" { __attribute__((visibility("default"))) __attribute__((used)) int32_t native_add(int32_t x, int32_t y) { return x + y; } __attribute__((visibility("default"))) __attribute__((used)) int32_t native_add2(int32_t x, int32_t y) { return x + y; } __attribute__((visibility("default"))) __attribute__((used)) char* concat(const char* str1, const char* str2) { int len1 = strlen(str1); int len2 = strlen(str2); char* result = new char[len1 + len2 + 1]; strcpy(result, str1); strcat(result, str2); return result; } } extern "C" __attribute__((visibility("default"))) __attribute__((used)) int32_t native_add3(int32_t x, int32_t y) { return x + y; }
新建android\CMakeLists.txt
cmake_minimum_required(VERSION 3.18.1) # for example add_library(native_lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). ../ios/Runner/native_lib.cpp )
app/build.gradle
externalNativeBuild { // Encapsulates your CMake build configurations. cmake { // Provides a relative path to your CMake build script. path "../CMakeLists.txt" } }
local.properties
ndk.dir=C:\sdk\androidsdk\Android\Sdk\ndk\24.0.8215888
native_add.dart
import 'dart:ffi' as ffi; import 'dart:ffi'; import 'dart:io'; // For Platform.isX import 'package:ffi/ffi.dart'; final DynamicLibrary nativeAddLib = Platform.isAndroid ? DynamicLibrary.open("libnative_lib.so") : DynamicLibrary.process(); final int Function(int x, int y) nativeAdd = nativeAddLib .lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add") .asFunction(); final int Function(int x, int y) nativeAdd2 = nativeAddLib .lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add2") .asFunction(); final int Function(int x, int y) nativeAdd3 = nativeAddLib .lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add3") .asFunction(); typedef A2 = Pointer<Utf8> Function(ffi.Pointer<Utf8>, ffi.Pointer<Utf8>); String concatStr(String str1, String str2) { final privateKeyPtr = str1.toNativeUtf8(); final hexCidMessagePtr = str2.toNativeUtf8(); final concat = nativeAddLib.lookup<ffi.NativeFunction<A2>>('concat').asFunction<A2>(); return concat(privateKeyPtr, hexCidMessagePtr).toDartString(); } bool isExistSymbol(String symbol) { final isOk = nativeAddLib.providesSymbol(symbol); return isOk; }
flutter 调用rust
创建 flutter项目或者插件
添加依赖
flutter_rust_bridge: ^1.71.0
ffi: ^2.0.1
ffigen: ^7.2.8
在根目录下创建rust项目
cargo new --lib native
拷贝api.rs,lib.rs
//lib.rs mod api; mod bridge_generated; //api.ts // This is the entry point of your Rust library. // When adding new code to your project, note that only items used // here will be transformed to their Dart equivalents. // A plain enum without any fields. This is similar to Dart- or C-style enums. // flutter_rust_bridge is capable of generating code for enums with fields // (@freezed classes in Dart and tagged unions in C). pub enum Platform { Unknown, Android, Ios, Windows, Unix, MacIntel, MacApple, Wasm, } // A function definition in Rust. Similar to Dart, the return type must always be named // and is never inferred. pub fn platform() -> Platform { // This is a macro, a special expression that expands into code. In Rust, all macros // end with an exclamation mark and can be invoked with all kinds of brackets (parentheses, // brackets and curly braces). However, certain conventions exist, for example the // vector macro is almost always invoked as vec![..]. // // The cfg!() macro returns a boolean value based on the current compiler configuration. // When attached to expressions (#[cfg(..)] form), they show or hide the expression at compile time. // Here, however, they evaluate to runtime values, which may or may not be optimized out // by the compiler. A variety of configurations are demonstrated here which cover most of // the modern oeprating systems. Try running the Flutter application on different machines // and see if it matches your expected OS. // // Furthermore, in Rust, the last expression in a function is the return value and does // not have the trailing semicolon. This entire if-else chain forms a single expression. if cfg!(windows) { Platform::Windows } else if cfg!(target_os = "android") { Platform::Android } else if cfg!(target_os = "ios") { Platform::Ios } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) { Platform::MacApple } else if cfg!(target_os = "macos") { Platform::MacIntel } else if cfg!(target_family = "wasm") { Platform::Wasm } else if cfg!(unix) { Platform::Unix } else { Platform::Unknown } } // The convention for Rust identifiers is the snake_case, // and they are automatically converted to camelCase on the Dart side. pub fn rust_release_mode() -> bool { cfg!(not(debug_assertions)) } pub fn test() -> String { String::from("这是一个rust函数") }
配置
Cargo.toml
[package] name = "native" version = "0.1.0" edition = "2021" [lib] name = "native" crate-type = ["staticlib", "cdylib"] [build-dependencies] flutter_rust_bridge_codegen = "=1.71.0" [dependencies] flutter_rust_bridge = "=1.71.0" flutter_rust_bridge_macros = "=1.71.0"
添加run_gen.sh
flutter_rust_bridge_codegen \ --rust-input native/src/api.rs \ --dart-output lib/bridge_generated.dart \ --c-output ios/Classes/bridge_generated.h
其他命令
cargo install flutter_rust_bridge_codegen
android相关
cargo install cargo-ndk
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
ios相关
64 bit targets (真机 & 模拟器):
rustup target add aarch64-apple-ios x86_64-apple-ios
New simulator target for Xcode 12 and later
ustup target add aarch64-apple-ios-sim
gradle.properties app/build.gradle
ANDROID_NDK=/Users/yujunlong/Library/Android/sdk/ndk/24.0.8215888
[ Debug: null, Profile: '--release', Release: '--release' ].each { def taskPostfix = it.key def profileMode = it.value tasks.whenTaskAdded { task -> if (task.name == "javaPreCompile$taskPostfix") { task.dependsOn "cargoBuild$taskPostfix" } } tasks.register("cargoBuild$taskPostfix", Exec) { workingDir "../../native" environment ANDROID_NDK_HOME: "$ANDROID_NDK" commandLine 'cargo', 'ndk', // the 2 ABIs below are used by real Android devices '-t', 'armeabi-v7a', '-t', 'arm64-v8a', '-t', 'x86', '-t', 'x86_64', '-o', '../android/app/src/main/jniLibs', 'build' if (profileMode != null) { args profileMode } } }
ios 配置
在native(rust项目下面)下面执行
cargo xcode
在 Xcode 中打开 ios/Runner.xcodeproj
xcode中选中Runner 然后File —> Add Files to “Runner”
native.xcodeproj
点击 Runner 根项目,TARGETS —> Build Phases —> Target Dependencies :请添加 native-staticlib
展开 Link Binary With Libraries:添加 libnative_static.a
Runner-Bridging-Header.h
#import "GeneratedPluginRegistrant.h" #import "bridge_generated.h"
AppDelegate.swift 添加
print(“dummy_value=(dummy_method_to_enforce_bundling())”);
import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { print("dummy_value=\(dummy_method_to_enforce_bundling())"); GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }