Flutter Foreign Function Interface with examples

Development
FFI, Flutter

Flutter FFI (Foreign Function Interface) is a powerful tool that allows developers to interact with native code written in languages such as C, C++, or Rust. In this blog post, we will explore some examples of how to use Flutter FFI.

Interacting with C Code

Let’s say we have a C library that we want to use in our Flutter app. We can use the FFI to create a bridge between the Flutter app and the C code. Here is an example of how to use the FFI to call a C function from Flutter:

First, we need to create a Dart file to define the functions we want to call from the C code:

import 'dart:ffi';

typedef add_func = Int32 Function(Int32 a, Int32 b);
typedef Add = int Function(int a, int b);

final DynamicLibrary nativeAddLib = DynamicLibrary.open('libnative_add.so');
final Add nativeAdd = nativeAddLib.lookup<NativeFunction>('native_add').asFunction();

This code defines a typedef for the C function native_add, loads the library libnative_add.so, and creates a Dart function called nativeAdd that calls the native_add function.

Next, we can use the nativeAdd function in our Flutter code like this:

import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int result = nativeAdd(2, 3);

    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Result: $result'),
        ),
      ),
    );
  }
}

This code calls the nativeAdd function with arguments 2 and 3 and displays the result in a Text widget.

Interacting with C++ Code

Interacting with C++ code is similar to interacting with C code. The main difference is that we need to use extern “C” to specify that the function should use C-style naming and calling conventions. Here is an example of how to use the FFI to call a C++ function from Flutter:
First, we need to create a header file to define the C++ function we want to call:

extern "C" {
  int native_multiply(int a, int b);
}

This code defines a C-style function called native_multiply that calls the C++ function multiply.

Next, we need to create a C++ file that implements the multiply function:

#include "example.h"

int multiply(int a, int b) {
  return a * b;
}

This code defines a C++ function called multiply that multiplies its two arguments.

Finally, we can use the FFI to call the native_multiply function from Flutter:

import 'dart:ffi';

typedef multiply_func = Int32 Function(Int32 a, Int32 b);
typedef Multiply = int Function(int a, int b);

final DynamicLibrary nativeMultiplyLib =
    DynamicLibrary.open('libnative_multiply.so');
final Multiply nativeMultiply =
    nativeMultiplyLib.lookup<NativeFunction>('native_multiply').asFunction();

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int result = nativeMultiply(2, 3);

    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Result: $result'),
        ),
      ),
    );
  }
}

This code loads the library libnative_multiply.so, defines a typedef for the native_multiply function, and calls it with the arguments 2 and 3, displaying the result in a Text widget.

Interacting with Rust Code

Interacting with Rust code is similar to interacting with C++ code. The main difference is that we need to use Rust’s #[no_mangle] attribute to specify that the function should use C-style naming and calling conventions. Here is an example of how to use the FFI to call a Rust function from Flutter:
First, we need to create a Rust file to define the function we want to call:

#[no_mangle]
pub extern "C" fn native_divide(a: i32, b: i32) -> i32 {
  a / b
}

This code defines a function called native_divide that divides its two arguments.

Next, we need to build a Rust library using cargo. Here is the Cargo.toml file:

[lib]
name = "native_divide"
crate-type = ["cdylib"]

This file tells cargo to build a dynamic library called native_divide.

Finally, we can use the FFI to call the native_divide function from Flutter:

import 'dart:ffi';
​
typedef divide_func = Int32 Function(Int32 a, Int32 b);
typedef Divide = int Function(int a, int b);
​
final DynamicLibrary nativeDivideLib = DynamicLibrary.open('libnative_divide.dylib');
final Divide nativeDivide = nativeDivideLib.lookup<NativeFunction>('native_divide').asFunction();
​
void main() {
  runApp(MyApp());
}
​
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int result = nativeDivide(6, 2);
​
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Result: $result'),
        ),
      ),
    );
  }
}

This code loads the library libnative_divide.dylib, defines a typedef for the native_divide function, and calls it with the arguments 6 and 2, displaying the result in a Text widget.

In conclusion, Flutter FFI is a powerful tool that allows developers to interact with native code written in languages such as C, C++, or Rust. With Flutter FFI, it’s possible to create a bridge between the Flutter app and the native code, making it easier to leverage existing libraries and improve app performance. By using the examples presented in this blog post, you can start exploring the possibilities of Flutter FFI in your own projects.