With a bit of configuration, the same code spans both Android and IOS, providing higher performance than the React Native solution. In addition, thanks to Rust cross-platform support, parts of Rust code can be reused in a variety of situations.
The purpose of this article is to document the author’s attempt to combine Rust and Flutter, and it is only a preliminary attempt. Does not involve such things as:
- How to build a Flutter development environment and how to use the Dart language
- How to build a Rust development environment and how to learn the Rust language
Environment
- Flutter: Android and IOS tools are properly configured
- Rust: Stable
Rust Part
Prepare cross-platform toolchains & deps
IOS
# Download targets for IOS ( 64 bit targets (real device & simulator) )
rustup target add aarch64-apple-ios x86_64-apple-ios
# Install cargo-lipo to generate the iOS universal library
cargo install cargo-lipo
Copy the code
Android
Here are some helpful auxiliary scripts to configure the cross-compile tool more quickly.
-
Obtain the Android the NDK
sdkmanager --verbose ndk-bundle Copy the code
If you have the Android NDK ready, set the environment variable $ANDROID_NDK_HOME
# example: export ANDROID_NDK_HOME=/Users/yinsiwei/Downloads/android-ndk-r20b Copy the code
-
Create the standalone NDK
# $(pwd) == ~/Downloads git clone https://github.com/kennytm/rust-ios-android.git cd rust-ios-android ./create-ndk-standalone.sh Copy the code
-
Configure the Android cross-build tool on Cargo Default Config VS
cat cargo-config.toml >> ~/.cargo/config Copy the code
In the Cargo default configuration, Add information about Android cross-platform targets (targets, AARCH64-linux-Android, armV7-linux-androideabi, i686-linux-Android) Point to the standalone NDK you just created.
[target.aarch64-linux-android] ar = ... linker = .. [target.armv7-linux-androideabi] ... [target.i686-linux-android] .. Copy the code
-
Download Rust to support Android cross-compilation dependencies
Copy the code
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android “`
Start a simple rust library
-
Create a Rust project
Copy the code
cargo init my-app-base –lib “`
-
Edit Cargo. Toml to modify Crate -type
Copy the code
[lib] name = “my_app_base” crate-type = [“staticlib”, “cdylib”] Support for building Staticlib is required; Android is running through dynamic links in the application runtime space, need to build cdylib support.
-
Write some C ABI compliant functions SRC /lib.rs
use std::os::raw::c_char; use std::ffi::CString; #[no_mangle] pub unsafe extern fn hello() - > *const c_char { let s = CString::new("world").unwrap(); s.into_raw() } Copy the code
In the code above, each time the hello function is called externally, a string (CString) is created in the Jincheng heap space and ownership (the right to free the heap space occupied by the string) is transferred to the caller.
Build libraries
# IOS
cargo lipo --release
# Android
cargo build --target aarch64-linux-android --release
cargo build --target armv7-linux-androideabi --release
cargo build --target i686-linux-android --release
Copy the code
You will then get the following useful materials in the target directory.
Target ├ ─ ─ aarch64 - Linux - android │ └ ─ ─ release │ ├ ─ ─ libmy_app_base. A │ └ ─ ─ libmy_app_base. So ├ ─ ─ Armv7 - Linux - androideabi │ └ ─ ─ release │ ├ ─ ─ libmy_app_base. A │ └ ─ ─ libmy_app_base. So ├ ─ ─ i686 - Linux - android │ └ ─ ─ Release │ ├ ─ ─ libmy_app_base. A │ └ ─ ─ libmy_app_base. So ├ ─ ─ the universal │ └ ─ ─ release │ └ ─ ─ libmy_app_base. ACopy the code
At this point, the Rust section ends.
Flutter Part
Copy build artifacts to flutter project
from: target/universal/release/libmy_app_base.a
to: ios/
from: target/aarch64-linux-android/release/libmy_app_base.so
to: android/app/src/main/jniLibs/arm64-v8a/
from: target/armv7-linux-androideabi/release/libmy_app_base.so
to: android/app/src/main/jniLibs/armeabi-v7a/
from: target/i686-linux-android/release/libmy_app_base.so
to: android/app/src/main/jniLibs/x86/
Copy the code
Call FFI function in Dart
-
Add the dependent
Pubspec. yaml -> dev_dependencies: += ffi: ^0.1.3
-
Add code
(Modify directly on the generated project, ignoring code design issues and simply running the project first)
import 'dart:ffi'; import 'package:ffi/ffi.dart'; // ... final dylib = Platform.isAndroid ? DynamicLibrary.open('libmy_app_base.so') :DynamicLibrary.process(); var hello = dylib.lookupFunction<Pointer<Utf8> Function(),Pointer<Utf8> Function(a) > ('hello'); // ... hello(); // -> world Copy the code
Build Android Project
flutter run # It will run directly if connected to an Android device
Copy the code
Build IOS Project
(Much more complicated)
- follow
Flutter
The official documentation, the configurationXCode
The project. - in
Build Phases
中Link Binary With Libraries
addlibmy_app_base.a
File (follow the arrow points on the graph…)
- in
Build Settings
中Other Linker Flags
addforce_load
The parameters.
This is due to the fact that the library functions are called dynamically in Dart, but these are useless functions that have never been called and are pruned out during static analysis at compile time. This is resolved by force_load.
Result
Troubleshooting
XCode & IOS
Error getting attached iOS device: ideviceinfo could not find device
sudo xattr -d com.apple.quarantine ~/flutter/bin/cache/artifacts/libimobiledevice/ideviceinfo
Copy the code
Replace the following path with yours
dyld: Library not loaded
dyld: Library not loaded: /b/s/w/ir/k/homebrew/Cellar/libimobiledevice-flutter/HEAD-398c120_3/lib/libimobiledevice.6.dylib
Referenced from: /Users/hey/flutter/bin/cache/artifacts/libimobiledevice/idevice_id
Reason: image not found
Copy the code
Delete & re-download
rm -rf /Users/hey/flutter/bin/cache && flutter doctor -v
Copy the code
The real machine cannot activate the Flutter program
See github.com/flutter/flu… Do not upgrade to IOS 13.3.1
What’s next
-
How to effectively implement Rust & Dart communication
We know that Flutter, like most GUI libraries, belongs to a single-threaded model combined with an event system, so code that calls Rust in the main thread using FFI cannot block the thread. The Dart language provides the async/await syntax feature for processing blocking tasks such as network requests in Flutter. Rust also provides async/await syntax support in recent releases, so how to gracefully combine the two parts is a problem.
-
Support for MacOS Windows Linux desktops
Flutter already has experimental support for desktop applications. See how it can be combined to share code across six ends.
References
-
Github.com/kennytm/rus…
Introduces how to build Android, IOS libraries, and provides examples
-
Github.com/TimNN/cargo…
Used to build the Universal Library
-
Github.com/hanabi1224/…
Idx0.dev /2020/02/15/…