Integrating Rust + Flutter + Kotlin for Mobile Applications

1,386

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)}');
}

enter image description here

Share:
1,386
Hasan A Yousef
Author by

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, 2022

Comments

  • Hasan A Yousef
    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:

    1. Main is in Flutter, that is calling native function through platform_channel
    2. The native function is calling rust library through JNI (JNI wrapper is required to be call the rust library)

    The structure is as below:

    enter image description here

    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
    Amit Rana almost 2 years
    Its working fine for Dart but when I tried same for Flutter app its not working. please help me out.