Calling Native Libraries in Flutter with Dart FFI

In this tutorial, you’ll learn how to use Dart FFI to access native libraries that support C-interoperability. By Nick Fisher.

5 (6) · 1 Review

Download materials
Save for later

Dart is a great language, but it doesn’t expose all the functionality you might need for a Flutter app. Your app might have performance-critical code that would benefit from being written in a lower-level language like C, C++ or Rust. Or you might need to link a specific external binary shared library, like TensorFlow or PyTorch Mobile.

Foreign function interface, or FFI, involves code written in one language interfacing with another language. Developers have been able to integrate Dart with native code in beta for some time. However, with Dart v2.13, Dart FFI is now available on the stable channel.

That’s great news for Flutter developers! If you’re new to FFI, now is the perfect time to explore taking your Flutter app beyond Dart.

In this tutorial, you’ll learn how to invoke native code by building WeatherFFI, a Flutter app that generates basic, albeit fake, weather data from a native library.

In the process, you’ll learn:

  1. About FFI and how it lets Dart code invoke code written in other languages.
  2. How FFI differs from Flutter platform channels.
  3. To automatically compile and link C code when building a Flutter app.
  4. How a Flutter app can use FFI to call code written in C.

Flutter mascot reading and writing a list

Before you start, consider what’s going on under the hood of a Flutter app.

Under the Hood of a Flutter App

In a Flutter app, Dart code runs alongside the Flutter framework, calculating widget sizes and drawing to the screen at around 60 frames per second:

Your Flutter app uses Flutter Framework and Dart to build apps illustration.

Your Dart code can use any of the functionality available in the Dart standard library, like opening a file or network connection.

Your Flutter app uses Dart to access functionality like the file system illustration.

But the Dart standard library only exposes a small amount of the functionality available to devices or operating systems.

For example, you can’t use Dart code to work directly with the speakers, camera or microphone. Working directly with these devices requires Java/Kotlin code on Android or Objective-C/Swift code on iOS. For the purposes of this tutorial, call this code platform code.

Sometimes, external plugins or packages let you write Dart code to work indirectly with these devices. These plugins or packages expose a Dart interface that you can use to invoke platform code over platform channels.

Platform Channels and Plugins

Dart code can’t interoperate with platform code directly. The Dart compiler can’t use the same types, memory layouts, function signatures and other lower-level conventions your platform code uses.

You need a bridge between the two that takes code Dart understands and translates it to code that Java, Kotlin, Swift or Objective-C understand.

The Flutter authors addressed this with plugins and platform channels, which let messages pass between Dart and platform code.

Flutter uses channels to access device-specific functionality illustration.

Most Flutter apps that introduce device-specific functionality use platform channels to bridge Dart and platform code.

Native Code

Some apps need greater control over memory management and garbage collection. For example, an app that uses 3D game engines such as Unreal Engine or a machine learning library like Tensorflow needs greater control.

Sometimes you need high performance code.

Developers generally write these apps in languages like C, C++ or Rust. For the purpose of this tutorial, call this native code.

For a Flutter app written in Dart to use a library written in native code, your app needs some way to bridge the two, just like Flutter platform channels act as a bridge between Dart code and platform code.

However, you can’t use platform channels since they only translate between Dart and platform code such as Java, Kotlin, Swift or Objective-C.

If you want to bridge between Dart code and native code, you need FFI.

Bridging Dart and Native Code with FFI

As mentioned earlier, FFI stands for Foreign Function Interface, a general term for any mechanism that lets developers write code in one programming language to invoke, or bridge, code written in another. It’s not just a Dart concept: many languages expose FFI to interoperate with binary libraries.

Developers write many of these binary libraries in C or C++ and distribute them as pre-compiled shared objects.

Flutter apps use dart:ffi library and ffi packages to integrate these libraries by bridging Dart code to native code.

In this tutorial, you’ll learn how to do this by writing WeatherFFI, a Flutter app that retrieves some simple weather forecast data from native C code.

This is obviously a contrived example. In the real world, a Flutter app would use FFI for something more like Snapchat’s Face Morph, real-time image recognition or video transcoding with FFmpeg.

But this simple example will show the fundamentals behind FFI and give you the tools to move on to more advanced use cases. With theory out of the way, it’s time to get started!

Getting Started

Download the starter project by clicking Download Materials at the top or bottom of the tutorial.

Note: This tutorial assumes you have Flutter 2.2 and Dart 2.13 or higher installed. Dart versions prior to v2.12.1 won’t work with this tutorial’s code.

This tutorial uses VS Code, but all tutorial steps should apply equally to Android Studio.

Android Setup

If you plan on using the Android Emulator or device, follow these steps to ensure Android Studio has both Android Native Development Kit, or NDK, and CMake installed. This applies to macOS, Linux and Windows.

NDK is an add-on to the Android SDK that enables the compilation of native code for Android apps. CMake is a tool that works with Gradle to use a native library.

Once you verify you installed NDK and CMake, you can use either VS Code or Android Studio.

macOS or iOS Setup

If you plan on using the Xcode Simulator or an Apple device, you already have everything you need for this tutorial as long as you followed the official Flutter documentation to install Xcode and configured the Xcode command-line tools.

It’s now time to start building your app.

Running WeatherFFI

Open the unzipped folder in your preferred IDE. Either run flutter pub get in the terminal, or open pubspec.yaml and click the Pub get tab that appears in your IDE. Once complete, open lib/main.dart.

Note: Ignore Dart Analysis errors stating Avoid lines longer than 80 characters. These are from lint rules in analysis_options.yaml. The project will still run and the errors will be addressed as you make updates.

Build and run.

WeatherFFI app main screen.

You’ll see four buttons:

  • Temperature retrieves the current numeric temperature.
  • Today’s forecast retrieves the descriptive forecast for the day.
  • 3-day forecast (Fahrenheit) retrieves the 3-day forecast in Fahrenheit.
  • 3-day forecast (Celsius) retrieves the 3-day forecast in Celsius.

Right now, these buttons throw an UnimplementedError() exception when tapped. Soon you’ll hook each one up to a C function via FFI. Once that’s done, you’ll know if tomorrow is a raincoat day or a swimsuit day!

Bright Sun Cartoon

Open pubspec.yaml. In the dependencies: section, locate # // TODO: Add ffi package here and replace it with the following, making sure it’s indented the same as flutter:

  ffi: ^1.0.0

This code lets you use Dart FFI. Now you’ll add your first native function.