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.

  1. 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
  2. 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
  3. 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
  4. 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

  1. Create a Rust project

    Copy the code

cargo init my-app-base –lib “`

  1. 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.

  1. 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

  1. Add the dependent

    Pubspec. yaml -> dev_dependencies: += ffi: ^0.1.3

  2. 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)

  1. followFlutter The official documentation, the configurationXCodeThe project.
  2. inBuild PhasesLink Binary With Librariesaddlibmy_app_base.aFile (follow the arrow points on the graph…)
  3. inBuild SettingsOther Linker Flagsaddforce_loadThe 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/…