Flutter is a popular framework for building cross-platform applications, including desktop applications. Flutter’s flexibility allows developers to use other programming languages, including C and Rust. Integrating C and Rust code with Flutter desktop applications can improve performance and provide access to low-level system resources.
In this article, we will discuss how to integrate C and Rust code -via FFI- on a Flutter desktop application. We assume that you already have experience with Dart & Flutter, you know how to set-up a project, and you are ready to dive deeper.
Setting up the Development Environment
Before we dive into the integration process, we need to set up the development environment. The following steps will help you set up the necessary tools for building and running Flutter desktop applications.
- Install Flutter by following the instructions provided on the official Flutter website
- Install Rust and Cargo, Rust’s package manager, by following the instructions provided on the official Rust website
- Install CMake, a cross-platform build system, by following the instructions provided on the official CMake website
- Install your favourite C and Rust extension for your IDE
Integrating C code with Flutter Desktop
In order to separate the main project from the C code, create a new plugin. You can use the Very Good CLI to generate a new plugin. This plugin can then be imported in your project. Integrating the C code differs slightly for each operating system. Next, we’ll discuss these differences in more detail.
For Linux
To integrate C code with Flutter desktop, we need to create a shared library that can be used by the Flutter application. We can achieve this by following the steps below.
- Create a new C project using your favorite C development environment.
- Add the necessary C code to the project and build it as a shared library.
- Move the shared library to the
linux
directory in the Flutter project. - Update the
CMakeLists.txt
file located in thelinux
folder to include the shared library in the project.
Here is an example of a CMakeLists.txt
file that includes a shared library:
cmake_minimum_required(VERSION 3.10) set(PROJECT_NAME "linux_c_plugin") set(LIBRARY_NAME "my_linux_library") project(${PROJECT_NAME} LANGUAGES CXX) set(PLUGIN_NAME "${PROJECT_NAME}_plugin") # List of absolute paths to libraries that should be bundled with the plugin. # This list could contain prebuilt libraries, or libraries created by an # external build triggered from this build file. set(${PROJECT_NAME}_bundled_libraries # This can be changed to accomodate different builds. $<TARGET_FILE:${LIBRARY_NAME}> PARENT_SCOPE )
In this example, we add the C shared library to the project using the set
command.
For Windows
The integration process for Windows is quite similar to Linux. Assuming that you already build your C code to a dynamic-link library, you only have to put the library in the windows
folder in your Flutter plugin and update the CMakeLists.txt
file. Make sure that the C functions that you want to use in Flutter are marked with __declspec(dllexport)
.
Here is an example of a CMakeLists.txt
file that includes a dynamic-link library:
cmake_minimum_required(VERSION 3.10) set(PROJECT_NAME "windows_c_plugin") set(LIBRARY_NAME "my_windows_library") project(${PROJECT_NAME} LANGUAGES CXX) set(PLUGIN_NAME "${PROJECT_NAME}_plugin") # List of absolute paths to libraries that should be bundled with the plugin. # This list could contain prebuilt libraries, or libraries created by an # external build triggered from this build file. set(${PROJECT_NAME}_bundled_libraries # This can be changed to accomodate different builds. $<TARGET_FILE:${LIBRARY_NAME}> PARENT_SCOPE )
For macOS
The integration process for macOS differs quite heavily compared to the Linux and Windows processes, since the macOS build process doesn’t use CMake. Assuming that you already build your C code to a dynamic library, you have to put the library in the macos
folder in your Flutter plugin. Next, we’ll update the .podspec
file in the macos
folder in the Flutter plugin.
Here is an example of a .podspec
file that includes a dynamic library:
Pod::Spec.new do |spec| spec.name = 'macos_c_plugin' spec.version = '1.0.0' spec.summary = 'A macOS Flutter plugin project with a linked C library.' spec.description = <<-DESC A macOS Flutter plugin project with a linked C library. DESC spec.homepage = 'https://unlockd.be/' spec.license = { :file => '../LICENSE' } spec.author = { 'Unlockd' => '[email protected]' } # This will ensure the source files in Classes/ are included in the native # builds of apps using this FFI plugin. Podspec does not support relative # paths, so Classes contains a forwarder C file that relatively imports # `../src/*` so that the C sources can be shared among all target platforms. spec.source = { :path => '.' } spec.vendored_libraries = 'my_macos_library.dylib' spec.dependency 'FlutterMacOS' spec.platform = :osx, '10.11' spec.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } spec.swift_version = '5.0' end
Integrating Rust Code with Flutter Desktop
Integrating Rust code with Flutter desktop applications is similar to integrating C code. We need to create a Rust library and link it with the Flutter application. You can follow the steps described above to link the compiled Rust library with your Flutter project.
In conclusion, integrating C and Rust code with Flutter desktop applications can be a powerful way to improve performance and access low-level system resources. By following the steps outlined in this article, you can easily create shared libraries in C or Rust and link them with your Flutter desktop application.
While there are some challenges involved in integrating C and Rust code with Flutter, such as setting up the development environment and updating the CMakeLists.txt
file, the benefits of doing so can make it worthwhile. With a little bit of effort, you can harness the power of C and Rust to create fast and efficient desktop applications with Flutter.