$30 off During Our Annual Pro Sale. View Details »

Seamless Flutter Native Integration: FFI & Pige...

JaiChangPark
November 22, 2024

Seamless Flutter Native Integration: FFI & Pigeon - Dreamwalker (JaichangPark / 박제창) @FlutterKaigi2024

- Seamless Flutter Native Integration: FFI & Pigeon
- Dreamwalker (Jaichangpark/박제창)
- FlutterKaigi2024
- https://2024.flutterkaigi.jp/session/e97ebf37-5753-43c9-83ea-2be68e7a4363
- https://2024.flutterkaigi.jp/

JaiChangPark

November 22, 2024
Tweet

More Decks by JaiChangPark

Other Decks in Programming

Transcript

  1. Flutter Build for any screen Flutter is an open source

    framework for building beautiful, natively compiled, multi-platform applications from a single codebase. / overview 3
  2. 5

  3. Platform channels • Method Channel ◦ A named channel for

    communicating with platform plugins using asynchronous method calls. • Event Channel ◦ A named channel for communicating with platform plugins using event streams. • Codec ◦ A message encoding/decoding mechanism. ▪ MessageCodec ▪ StandardMessageCodec ▪ BinaryCodec ▪ JSONMessageCodec ▪ StringCodec 11 @source: https://docs.flutter.dev/resources/architectural-overview#integrating-with-other-code
  4. FlutterActivity package com.dreamwalker.untitled import io.flutter.embedding.android.FlutterActivity class MainActivity: FlutterActivity(){ override fun

    configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) // code ... } } 13
  5. FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin class CustomFlutterPlugin : FlutterPlugin { override fun

    onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { // Initialization and method channel setup for the plugin // code.. } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { methodChannel.setMethodCallHandler(null) } } 14
  6. FlutterActivity vs FlutterPlugin FlutterActivity FlutterActivity is an Android Activity that

    displays the Flutter application UI and starts the Flutter engine. You would use FlutterActivity when you want to show Flutter UI within an Android native application. FlutterPlugin FlutterPlugin is an interface for communication between Flutter and the native platform (e.g., Android, iOS). You would implement FlutterPlugin when you want to call native APIs or handle specific events in a Flutter application. 15
  7. / Platform-Specific Code Design • plugin_platform_interface • Pigeon • FFI(Foreign

    function interface) → C • FFIgen • JNI (Java Native Interface) → Java & Kotlin • JNIgen (Java Native Interface) • and other bridges Interoperability 17 Methods : How to
  8. / Platform-Specific Code Design Writing a good Flutter plugin 18

    • Think functionality first, then APIs • Avoid platform-specific API methods • Avoid supporting only one platform • Make your plugin easy to navigate and test • Avoid writing static (or global) methods @sourece : https://medium.com/flutter/writing-a-good-flutter-plugin-1a561b986c9c
  9. / Platform-Specific Code Design 1. Requirements Definition 2. Identify required

    features 3. Data type & structure design 4. Platform code development 5. UI code development Design Checklist 19
  10. / Platform-Specific Code Design Channel Name Design Type Detail Channel

    Name Battery Battery Level MethodChannel com.dreamwalker.flutterkaigi2024/battery ${Common}/battery Battery Charging Status EventChannel com.dreamwalker.flutterkaigi2024/charging Sensor Gyroscope EventChannel com.dreamwalker.flutterkaigi2024/sensors/gyroscope ${Common}/sensors/gyroscope Common package or domain com.dreamwalker.flutterkaigi2024 21
  11. Common (Flutter, Host(Native)) private val BATT_CHANNEL = "com.dreamwalker.flutterkaigi2024/battery" private val

    BATT_CHARGING_CHANNEL = "com.dreamwalker.flutterkaigi2024/charging" private val GYROSCOPE_CHANNEL = "com.dreamwalker.flutterkaigi2024/sensors/gyroscope" Host (Native) static const battMethodChannel = MethodChannel('com.dreamwalker.flutterkaigi2024/battery'); static const EventChannel battEventChannel = EventChannel('com.dreamwalker.flutterkaigi2024/charging'); static const EventChannel gyroscopeEventChannel = EventChannel('com.dreamwalker.flutterkaigi2024/sensors/gyroscope); Flutter 22
  12. / Platform-Specific Code Design Date Structure & Tpye Design Type

    Detail Data Type Battery Battery Level INT Sensor Gyroscope X Double Gyroscope Y Double Gyroscope Z Double Gyroscope List<Double> 23
  13. Host (Native) - Android / Kotlin MethodChannel( flutterEngine.dartExecutor.binaryMessenger, BATT_CHANNEL, ).setMethodCallHandler

    { call, result -> if (call.method == "getBatteryLevel") { CoroutineScope(Dispatchers.Main).launch { val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available.", null) } } } else { result.notImplemented() } } 25
  14. Host (Native) - Android / Kotlin MethodChannel( flutterEngine.dartExecutor.binaryMessenger, BATT_CHANNEL, ).setMethodCallHandler

    { call, result -> if (call.method == "getBatteryLevel") { CoroutineScope(Dispatchers.Main).launch { val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available.", null) } } } else { result.notImplemented() } } 26
  15. • Configures, bootstraps, and starts executing Dart code. • Once

    started, a DartExecutor cannot be stopped. The associated Dart code will execute until it completes, or until the FlutterEngine that owns this DartExecutor is destroyed. • Creates a FlutterEngine in this group and run its DartExecutor with a default entrypoint of the "main" function in the "lib/main.dart" file. DartExecutor 27
  16. Host (Native) - Android / Kotlin MethodChannel( flutterEngine.dartExecutor.binaryMessenger, BATT_CHANNEL, ).setMethodCallHandler

    { call, result -> if (call.method == "getBatteryLevel") { CoroutineScope(Dispatchers.Main).launch { val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available.", null) } } } else { result.notImplemented() } } 28
  17. Host (Native) - Android / Kotlin MethodChannel( flutterEngine.dartExecutor.binaryMessenger, BATT_CHANNEL, ).setMethodCallHandler

    { call, result -> if (call.method == "getBatteryLevel") { CoroutineScope(Dispatchers.Main).launch { val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available.", null) } } } else { result.notImplemented() } } 29
  18. Host (Native) - Android / Kotlin MethodChannel( flutterEngine.dartExecutor.binaryMessenger, BATT_CHANNEL, ).setMethodCallHandler

    { call, result -> if (call.method == "getBatteryLevel") { CoroutineScope(Dispatchers.Main).launch { val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available.", null) } } } else { result.notImplemented() } } 30
  19. Host (Native) - Android / Kotlin MethodChannel( flutterEngine.dartExecutor.binaryMessenger, BATT_CHANNEL, ).setMethodCallHandler

    { call, result -> if (call.method == "getBatteryLevel") { CoroutineScope(Dispatchers.Main).launch { val batteryLevel = getBatteryLevel() if (batteryLevel != -1) { result.success(batteryLevel) } else { result.error("UNAVAILABLE", "Battery level not available.", null) } } } else { result.notImplemented() } } 31
  20. Host (Native) - Android / Kotlin class SensorPlugin : FlutterPlugin

    { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { val sensorsManager = binding.applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager gyroscopeChannel = EventChannel(binding.binaryMessenger, GYROSCOPE_CHANNEL) gyroscopeChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventSink?) { } override fun onCancel(arguments: Any?) { } }) } 32
  21. Host (Native) - Android / Kotlin class SensorPlugin : FlutterPlugin

    { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { val sensorsManager = binding.applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager gyroscopeChannel = EventChannel(binding.binaryMessenger, GYROSCOPE_CHANNEL) gyroscopeChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventSink?) { } override fun onCancel(arguments: Any?) { } }) } 33
  22. Host (Native) - Android / Kotlin class SensorPlugin : FlutterPlugin

    { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { val sensorsManager = binding.applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager gyroscopeChannel = EventChannel(binding.binaryMessenger, GYROSCOPE_CHANNEL) gyroscopeChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventSink?) { } override fun onCancel(arguments: Any?) { } }) } 34
  23. Host (Native) - Android / Kotlin class SensorPlugin : FlutterPlugin

    { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { val sensorsManager = binding.applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager gyroscopeChannel = EventChannel(binding.binaryMessenger, GYROSCOPE_CHANNEL) gyroscopeChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventSink?) { } override fun onCancel(arguments: Any?) { } }) } 35
  24. Host (Native) - Android / Kotlin class SensorPlugin : FlutterPlugin

    { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { val sensorsManager = binding.applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager gyroscopeChannel = EventChannel(binding.binaryMessenger, GYROSCOPE_CHANNEL) gyroscopeChannel.setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(arguments: Any?, events: EventSink?) { } override fun onCancel(arguments: Any?) { } }) } 36
  25. Host (Native) - Android / Kotlin FlutterPlugin private fun sensorEventListener(events:

    EventChannel.EventSink): SensorEventListener { return object : SensorEventListener { override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} override fun onSensorChanged(event: SensorEvent) { val sensorValues = DoubleArray(event.values.size) event.values.forEachIndexed { index, value -> sensorValues[index] = value.toDouble() } events.success(sensorValues) } } } 37
  26. Host (Native) - Android / Kotlin FlutterPlugin private fun sensorEventListener(events:

    EventChannel.EventSink): SensorEventListener { return object : SensorEventListener { override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} override fun onSensorChanged(event: SensorEvent) { val sensorValues = DoubleArray(event.values.size) event.values.forEachIndexed { index, value -> sensorValues[index] = value.toDouble() } events.success(sensorValues) } } } 38
  27. Host (Native) - Android / Kotlin FlutterPlugin private fun sensorEventListener(events:

    EventChannel.EventSink): SensorEventListener { return object : SensorEventListener { override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} override fun onSensorChanged(event: SensorEvent) { val sensorValues = DoubleArray(event.values.size) event.values.forEachIndexed { index, value -> sensorValues[index] = value.toDouble() } events.success(sensorValues) } } } 39
  28. Host (Native) - Android / Kotlin class MainActivity : FlutterActivity()

    { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) flutterEngine.plugins.add(SensorPlugin()) MethodChannel( flutterEngine.dartExecutor.binaryMessenger, CHANNEL, ).setMethodCallHandler { call, result -> // code .. } } } You can easily add other Flutter plugins 40
  29. Flutter : MethodChannel 41 Future<void> getBatteryLevel() async { String batteryLevel;

    try { final result = await platform.invokeMethod<int>('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { // code.. }); } method channel
  30. Flutter : MethodChannel 42 Future<void> getBatteryLevel() async { String batteryLevel;

    try { final result = await platform.invokeMethod<int>('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { // code.. }); } int int?
  31. Flutter : MethodChannel 43 Future<void> getBatteryLevel() async { String batteryLevel;

    try { final result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { // code.. }); } dynamic
  32. Flutter : EventChannel 44 Stream<GyroscopeEvent>? gyroscopeEvents; static const EventChannel gyroscopeEventChannel

    = EventChannel('com.xxx.xxx/sensors/gyroscope'); gyroscopeEvents ??= gyroscopeEventChannel.receiveBroadcastStream().map((dynamic event) { final list = event.cast<double>(); return GyroscopeEventData( list[0]!, list[1]!, list[2]! ); });
  33. Flutter : EventChannel 45 Stream<GyroscopeEvent>? gyroscopeEvents; static const EventChannel gyroscopeEventChannel

    = EventChannel('com.xxx.xxx/sensors/gyroscope'); gyroscopeEvents ??= gyroscopeEventChannel.receiveBroadcastStream().map((dynamic event) { final list = event.cast<double>(); return GyroscopeEventData( list[0]!, list[1]!, list[2]! ); });
  34. Pigeon is a code generator tool to make communication between

    Flutter and the host platform type-safe, easier, and faster. • Pigeon removes the necessity to manage strings across multiple platforms and languages. • It also improves efficiency over common method channel patterns. • Most importantly though, it removes the need to write custom platform channel code, since pigeon generates it for you. Code generator tool Pigeon 58
  35. Currently pigeon supports generating: • Kotlin and Java code for

    Android • Swift and Objective-C code for iOS and macOS • C++ code for Windows • GObject code for Linux Supported Platforms Pigeon 59
  36. flutter packages Use Cases 61 • shared_preferences • url_launcher •

    image_picker • camera • in_app_purchase • video_player • and so on / Pigeon
  37. 66 enum AlgorithmType { sha1, sha2, md5, aesCbc, aesEcb, }

    class MessageData { MessageData({required this.type, required this.data}); AlgorithmType type; String? data; } 2. Define Messages
  38. 67 @HostApi() abstract class CustomHostApi { String generateHash(MessageData message); String

    encrypt(MessageData message); @async String decrypt(MessageData message); } 3. Define APIs
  39. @HostApi • @HostApi() being for procedures that are defined on

    the host platform. • When you want to call the host platform from Flutter. @FlutterApi • @FlutterApi() for procedures that are defined in Dart. • When you want to call a Flutter app from the host platform. 68 / Pigeon
  40. 69 4. Code Generation dart run pigeon \ --input pigeons/messaging.dart

    \ --dart_out lib/pigeon.dart \ --objc_header_out ios/Runner/pigeon.h \ --objc_source_out ios/Runner/pigeon.m \ --java_out ./android/app/src/main/java/com/example/xxxx/Pigeon.java \ --java_package "com.example.xxxx"
  41. 70 @ConfigurePigeon(PigeonOptions( dartOut: 'lib/src/messages.g.dart', dartOptions: DartOptions(), cppOptions: CppOptions(namespace: 'flutter_pigeon'), cppHeaderOut:

    'windows/runner/messages.g.h', cppSourceOut: 'windows/runner/messages.g.cpp', kotlinOut: 'android/app/src/main/kotlin/com/dreamwalker/flutter_pigeon/Messages.g.kt', kotlinOptions: KotlinOptions(), javaOut: 'android/app/src/main/java/io/flutter/plugins/Messages.java', javaOptions: JavaOptions(), swiftOut: 'ios/Runner/Messages.g.swift', swiftOptions: SwiftOptions(), objcHeaderOut: 'macos/Runner/messages.g.h', objcSourceOut: 'macos/Runner/messages.g.m', objcOptions: ObjcOptions(prefix: 'PGN'), copyrightHeader: 'pigeons/copyright.txt', dartPackageName: 'flutter_pigeon', )) 4. Code Generation message.dart
  42. 71 @ConfigurePigeon(PigeonOptions( dartOut: 'lib/src/messages.g.dart', dartOptions: DartOptions(), cppOptions: CppOptions(namespace: 'flutter_pigeon'), cppHeaderOut:

    'windows/runner/messages.g.h', cppSourceOut: 'windows/runner/messages.g.cpp', kotlinOut: 'android/app/src/main/kotlin/com/dreamwalker/flutter_pigeon/Messages.g.kt', kotlinOptions: KotlinOptions(), javaOut: 'android/app/src/main/java/io/flutter/plugins/Messages.java', javaOptions: JavaOptions(), swiftOut: 'ios/Runner/Messages.g.swift', swiftOptions: SwiftOptions(), objcHeaderOut: 'macos/Runner/messages.g.h', objcSourceOut: 'macos/Runner/messages.g.m', objcOptions: ObjcOptions(prefix: 'PGN'), copyrightHeader: 'pigeons/copyright.txt', dartPackageName: 'flutter_pigeon', ))
  43. 72 @ConfigurePigeon(PigeonOptions( dartOut: 'lib/src/messages.g.dart', dartOptions: DartOptions(), cppOptions: CppOptions(namespace: 'flutter_pigeon'), cppHeaderOut:

    'windows/runner/messages.g.h', cppSourceOut: 'windows/runner/messages.g.cpp', kotlinOut: 'android/app/src/main/kotlin/com/dreamwalker/flutter_pigeon/Messages.g.kt', kotlinOptions: KotlinOptions(), javaOut: 'android/app/src/main/java/io/flutter/plugins/Messages.java', javaOptions: JavaOptions(), swiftOut: 'ios/Runner/Messages.g.swift', swiftOptions: SwiftOptions(), objcHeaderOut: 'macos/Runner/messages.g.h', objcSourceOut: 'macos/Runner/messages.g.m', objcOptions: ObjcOptions(prefix: 'PGN'), copyrightHeader: 'pigeons/copyright.txt', dartPackageName: 'flutter_pigeon', )) com.dreamwalker.flutter_pigeon
  44. 73 @ConfigurePigeon(PigeonOptions( dartOut: 'lib/src/messages.g.dart', dartOptions: DartOptions(), cppOptions: CppOptions(namespace: 'flutter_pigeon'), cppHeaderOut:

    'windows/runner/messages.g.h', cppSourceOut: 'windows/runner/messages.g.cpp', kotlinOut: 'android/app/src/main/kotlin/com/dreamwalker/flutter_pigeon/Messages.g.kt', kotlinOptions: KotlinOptions(), javaOut: 'android/app/src/main/java/io/flutter/plugins/Messages.java', javaOptions: JavaOptions(), swiftOut: 'ios/Runner/Messages.g.swift', swiftOptions: SwiftOptions(), objcHeaderOut: 'macos/Runner/messages.g.h', objcSourceOut: 'macos/Runner/messages.g.m', objcOptions: ObjcOptions(prefix: 'PGN'), copyrightHeader: 'pigeons/copyright.txt', dartPackageName: 'flutter_pigeon', ))
  45. 76 final CustomHostApi _api = CustomHostApi(); Future<String> genSha2(String input) async

    { try { final v = await _api.generateHash( MessageData(type: AlgorithmType.sha2, data: input), ); } catch (e) { return "$e"; } } 5. Flutter Side
  46. 77 private class CustomApiImpl : CustomHostApi { override fun generateHash(message:

    MessageData): String { //code... } override fun encrypt(message: MessageData): String { TODO("Not yet implemented") } override fun decrypt(message: MessageData, callback: (Result<String>) -> Unit) { TODO("Not yet implemented") } } 5. Android (Kotlin) CustomHostApi: Generated Code by pigeon
  47. 78 private class CustomApiImpl : CustomHostApi { override fun generateHash(message:

    MessageData): String { //code... } override fun encrypt(message: MessageData): String { TODO("Not yet implemented") } override fun decrypt(message: MessageData, callback: (Result<String>) -> Unit) { TODO("Not yet implemented") } } 5. Android (Kotlin) @async
  48. 79 class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine)

    { super.configureFlutterEngine(flutterEngine) val api = CustomApiImplementation() CustomHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api) } } 5. Android (Kotlin)
  49. 82 private class CustomApiImpl: CustomHostApi { func generateHash(message: MessageData) throws

    -> String { } func encrypt(message: MessageData) throws -> String { } func decrypt(message: MessageData, completion: @escaping (Result<String, Error>) -> Void) { } } 5. iOS (swift) Using CryptoKit
  50. 83 @main @objc class AppDelegate: FlutterAppDelegate { override func application(

    _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) let controller = window?.rootViewController as! FlutterViewController let api = CustomApiImpl() CustomHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } 5. iOS (swift)
  51. 86 / Foreign Function Interface • FFI stands for foreign

    function interface. • Dart mobile, command-line, and server apps running on the Dart Native platform can use the dart:ffi library to call native C APIs, and to read, write, allocate, and deallocate native memory. • ffigen only supports parsing C headers, not C++ headers.
  52. 87 / Foreign Function Interface • Databases • Graphic &

    Image processing • OS (Native) API → Windows, Linux etc. • C-based compiled libs
  53. 89 fibonacci #ifndef FIBONACCI_H #define FIBONACCI_H int fibonacci(int n); #endif

    // FIBONACCI_H fibonacci.h @Source https://en.wikipedia.org/wiki/Fibonacci_sequence By Romain - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=114415511
  54. 90 fibonacci int fibonacci(int n) { if (n <= 0)

    { return 0; } else if (n == 1) { return 1; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } fibonacci.c
  55. 93 ! Key Point final DynamicLibrary _dylib = () {

    if (Platform.isMacOS || Platform.isIOS) { return DynamicLibrary.open('$_libName.framework/$_libName'); } if (Platform.isAndroid || Platform.isLinux) { return DynamicLibrary.open('lib$_libName.so'); } if (Platform.isWindows) { return DynamicLibrary.open('$_libName.dll'); } throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); }();
  56. 94 fibonacci main.dart final fibo = dylib .lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int)>>('fibonacci') .asFunction<int

    Function(int)>(); lib(Native) Function Client (Dart) Function function name Data Type of parameters Return Data Type
  57. 97 #[no_mangle] pub extern "C" fn fibonacci(n: i32) -> i32

    { if n <= 0 { return 0; } else if n == 1 { return 1; } else { let mut a = 0; let mut b = 1; let mut c = 0; for _ in 2..=n { c = a + b; a = b; b = c; } return c; } cargo build --release --target=[target] Android cargo ndk -t [target] -o ./jniLibs build --release • arm64-v8a • armeabi-v7a • x86 • x86_64
  58. 98 / Foreign Function Interface fibonacci Rust based Android Rust

    logo © Rust Foundation, used under CC-BY 4.0.
  59. 99 _dylib = () { if (Platform.isAndroid || Platform.isLinux) {

    return ffi.DynamicLibrary.open('librust_lib.so'); } throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); }();
  60. 100 typedef FibonacciFunc = ffi.Int Function(ffi.Int); typedef Fibonacci = int

    Function(int); final Fibonacci fibonacci = _dylib .lookup<ffi.NativeFunction<FibonacciFunc>>('fibonacci') .asFunction(); int n = 10; int result = fibonacci(n);
  61. 101 / Foreign Function Interface Generator for FFI bindings, using

    LibClang to parse C, Objective-C, and Swift files. ffigen
  62. 103 ffigen name: NativeLibrary description: misc output: 'generated_bindings.dart' headers: entry-points:

    - 'lib/fibonacci/fibonacci.h' include-directives: - 'lib/fibonacci/fibonacci.h' ffigen.yaml Class Name
  63. 106 / Summary • Platform-Specific Code ◦ Method Channel, Event

    Channel ◦ plugin_platform_interface ◦ ffi, ffigen, jnigen, pigeon, etc. • Pigeon ◦ Code generator tool ◦ type-safe • FFI ◦ FFI stands for foreign function interface. ◦ Lookup, asFunction ◦ ffigen