floui-rs
Rust bindings for floui, pronounced "flowy", a proof-of-concept single header C++17 lib inspired by SwiftUI, which wraps native iOS and Android controls/widgets, and integrates into the de facto build environments of each platform (XCode and Android Studio).
Currently available controls:
- Text
- TextField
- Button
- VStack (Vertical UIStackView on iOS and LinearLayout on Android)
- HStack (Horizontal UIStackView on iOS and LinearLayout on Android)
- Spacer
- Toggle/Check (tvOS doesn't support it)
- Slider (tvOS doesn't support it)
- ImageView
- WebView
- ScrollView
Usage
You can check out the floui-rs-template, which is structured to be able to build for both ios or android from the command-line.
If you would like to build purely for iOS and only using Rust (no Objective-C), check the example here.
Otherwise, if you would like to do it manually:
Build your library as a static-lib:
# Cargo.toml
[]
= ["static-lib"]
[]
= "0.1"
Build against the android and ios architectures you might need.
Rust
use ;
use RefCell;
use Rc;
use c_void;
extern "C"
extern "C"
Notes on certain usages
-
Sliders on Android take the full width of the LinearLayout, so this must be taken into consideration if code is shared also with iOS.
-
Adding images has to be in the project's resource file.
- In Android Studio: Resource Manager, Import Drawables. This will add the file to res/drawable. The file can be accessed directly ImageView::load("MyImage.jpg").
- In XCode: You can simply drag images into Assets.xcassets, then the image can be accessed directly ImageView::load("MyImage.jpg").
-
Using the WebView widget
- on iOS:
- Requires adding WebKit.framework under General > Frameworks, Libraries and Embedded Content.
- Requires enabling the
ios-webview
flag in your Cargo.toml. - Local files can be loaded using WebView::load_url() but need to be preceded by
file:///
, the files should be added to your xcode project.
- On Android:
- To load local files, precede them with
file:///
and the path of the file, which should be added to an assets folder (File > New > Folder > Assets folder). This then can be loaded using WebView::load_url(). - To load http requests, you need to enable the internet permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
- To load local files, precede them with
- on iOS:
Creating new widgets
Wrapping platform widgets doesn't require using C++, it requires that the widget implements WidgetExt, and the underlying (JNI jobject pointer for Android, UIView on iOS) can be retrieved. You can use the jni-rs crate and objc crates for such purposes.
Target-specific structure
iOS
- Add the required ios rustup targets.
- Install cargo-lipo.
- Add the built library to your xcode project (under Build Phases > Link Binary with Libraries).
- Modify the library search path to find the library (under Build Settings > Library Search Paths).
- Modify your ViewController.m file:
#import "ViewController.h"
extern void *floui_main(void *, void *, void *);
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
floui_main((void *)CFBridgingRetain(self), nil, nil);
}
@end
Android
- ANDROID_SDK_ROOT should be set to your android sdk directory.
- ANDROID_NDK_ROOT should be set to your android ndk directory.
- Add the required android rustup targets.
- Create an Android Studio Native C++ project, choose toolchain C++ 17 in the last step.
- Modify your CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
project(myapplication)
find_library(log-lib log)
add_library(myapplication SHARED native-lib.cpp)
add_library(rust-lib STATIC IMPORTED)
if (ANDROID_ABI STREQUAL x86)
set(RUST_ARCH i686-linux-android)
elseif (ANDROID_ABI STREQUAL armeabi-v7a)
set(RUST_ARCH armv7-linux-androideabi)
elseif (ANDROID_ABI STREQUAL arm64-v8a)
set(RUST_ARCH aarch64-linux-android)
elseif (ANDROID_ABI STREQUAL x86_64)
set(RUST_ARCH x86_64-linux-android)
else ()
message(FATAL "Unknown architecture")
endif ()
set_property(TARGET rust-lib PROPERTY IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_LIST_DIR}/app/target/${RUST_ARCH}/debug/libapp.a)
set_property(TARGET rust-lib PROPERTY IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_LIST_DIR}/app/target/${RUST_ARCH}/release/libapp.a)
target_link_libraries(myapplication android rust-lib ${log-lib})
- Modify your C++ file to just call the Rust lib.
extern "C" void *;
extern "C" void ;
extern "C" JNIEXPORT jobject JNICALL
extern "C" JNIEXPORT void JNICALL
- Modify your MainActivity.java to look like:
;
;
;
;
;
;
;