Integrating Rust + Flutter + Kotlin for Mobile Applications
With the introduction of ffi in Dart, things became more smoother now, with a better performance as the interction now is Dart/Rust directly, without a need for Dart/Kotlin/Rust or Dart/Swift/Rust cycle, below a simple example:
First src/lib.rs
#[no_mangle]
pub extern fn rust_fn(x: i32) -> i32 {
println!("Hello from rust\nI'll return: {}", x.pow(2));
x.pow(2)
}
and Cargo.toml
[package]
name = "Double_in_Rost"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"
[lib]
name = "rust_lib"
crate-type = ["dylib"] # could be `staticlib` as well
[dependencies]
Running cargo build --release
will generate target\release\rust_lib.dll
copy/paste it into Dart application root directory
Write Dart code as below:
import 'dart:ffi';
import 'dart:io' show Platform;
// FFI signature of the hello_world C function
typedef ffi_func = Int32 Function(Int32 x); //pub extern fn rust_fn(x: i32) -> i32
// Dart type definition for calling the C foreign function
typedef dart_func = int Function(int x);
void main() {
// Open the dynamic library
var path = './rust_lib.so';
if (Platform.isMacOS) path = './rust_lib.dylib';
if (Platform.isWindows) path = 'rust_lib.dll';
final dylib = DynamicLibrary.open(path);
// Look up the Rust/C function
final my_func =
dylib.lookup<NativeFunction<ffi_func>>('rust_fn').asFunction<dart_func>();
print('Double of 3 is ${my_func(3)}');
}
Hasan A Yousef
Industrial Engineer, with a slogan "If you can automate it, do not do it". Interested in all types of automation that facilitate life. Work officially as Supply Chain Manager, where Data is the main driver of availability. Combining my IT skills and interest with my work expertise, proud to deliver the best possible results for my organization.
Updated on November 26, 2022Comments
-
Hasan A Yousef over 1 year
As next week will have importat launch for Rust 2018 and Flutter 1.0, I thought to build an app using Rust for the business logic and Flutter for the user interface, that can run at both Android and iOS, I built one and tested it at Android and it is working fine.
I just wonder how to measure the performance and compare it with native Android/iOS app.
The app flow is:
- Main is in Flutter, that is calling native function through platform_channel
- The native function is calling rust library through JNI (JNI wrapper is required to be call the rust library)
The structure is as below:
The code used is:
main.dart
:import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { static const platform = const MethodChannel('samples.flutter.io/battery'); String _batteryLevel = 'Unknown battery level.'; Future<void> _getBatteryLevel() async { String batteryLevel; try { final String hello = await platform.invokeMethod('getText'); final int result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = '$hello Battery level at $result %.'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { _batteryLevel = batteryLevel; }); } @override Widget build(BuildContext context) { return Material( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ RaisedButton( child: Text('Get Battery Level'), onPressed: _getBatteryLevel, ), Text(_batteryLevel), ], ), ), ); } }
JNI wrapper - RustGreetings.kt
package com.mozilla.greetings class RustGreetings { companion object { init { System.loadLibrary("greetings") } } private external fun greeting(pattern: String): String fun sayHello(to: String): String = greeting(to) }
And the Main Android activity is:
package com.example.batterylevel import android.os.Bundle import io.flutter.app.FlutterActivity import io.flutter.plugins.GeneratedPluginRegistrant import io.flutter.plugin.common.MethodChannel import android.content.Context import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager import android.os.Build.VERSION import android.os.Build.VERSION_CODES import lib.Library import com.mozilla.greetings.RustGreetings class MainActivity: FlutterActivity() { private val CHANNEL = "samples.flutter.io/battery" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) GeneratedPluginRegistrant.registerWith(this) MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getText") { result.success(getText()) } else if (call.method == "getBatteryLevel") { // result.success(getText()) val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available.", null) } } else { result.notImplemented() } } } private fun getBatteryLevel(): Int { val batteryLevel: Int if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) } else { val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) } return batteryLevel } private fun getText(): String { val x = Library().someLibraryMethod() val g = RustGreetings() val r = g.sayHello("My $x Rust") return r } }
In the Android
gradle.build
I just added the below, as I'm interested to check also the impact of adding kotlin JVM library and getting it interacted with the Rust library within the mobile application:dependencies { implementation(files("src/main/libs/lib.jar")) }
My question is: How can check the performance and impact of each process when it is executed or called by another process
-
Amit Rana almost 2 yearsIts working fine for Dart but when I tried same for Flutter app its not working. please help me out.