Website: https://web.flatteredwithflutter.com/#/
Pre-Requisite: Install package FFI
This article assumes that the reader has knowledge about FFI. In case not, check this.
We will cover briefly about
- Creating C Library
- Integrate C Library in Flutter MacOS
- Call from Flutter MacOS
Note: We won’t be explaining in-depth about Dart FFI as there are good articles dedicated to it.
Article here: https://flatteredwithflutter.com/flutterdesktop-and-c/
Creating C Library
Every complicated thing starts with a basic thing. Let’s create a simple C library (Hello World)
Introducing CMake and makefile
Creating a c library involves a bunch of commands, either you can use those or you can utilize CMake.
As per the documentation
CMake is an extensible, open-source system that manages the build process in an operating system and in a compiler-independent manner.
Install CMake using
brew install cmake
CMake uses a text file named CMakeLists.txt, in the home directory of the software package, to specify the actions that are necessary to set up the software. The user begins the build process by moving to the home directory and typing
cmake .
CMake will carry out a configuration process, after which a traditional Makefile will be created in the home directory. The software process is then completed by typing
make
Create a C header file, hello.h, which has 1 function declaration
void hello_world();
Let’s import this header file in our hello.c
#include <stdio.h> #include "hello.h" // OUR HEADER FILE int main() { hello_world(); // FUNCTION FROM HEADER FILE return 0; } void hello_world() // IMPLEMENTATION OF FUNCTION { printf("Hello World\n"); }
Our folder structure looks like this
Finally, inside our CMakeLists.txt we define the commands as
cmake_minimum_required(VERSION 3.10) project(first_c VERSION 1.0 LANGUAGES C) add_library(first_c SHARED hello.c) add_executable(hello_test hello.c) set_target_properties(first_c PROPERTIES PUBLIC_HEADER hello.h VERSION ${PROJECT_VERSION} SOVERSION 1 OUTPUT_NAME "hello" )
Note: CMake commands list
- cmake_minimum_required: Set the minimum required version of CMake for a project.
- project: Set a name, version, and enable languages for the entire project.
Here, we are using first_c (our parent folder name), version 1.0, and language as C.
- add_library: Add a library to the project using the specified source files.
Our project is first_c and we specify Shared (Dynamic Library) to be created out of our hello.c program
- add_executable: Add an executable to the project using the specified source files.
This creates an executable(hello_test) from our source (hello.c), which we can test for our generated shared library.
- set_target_properties: Targets can have properties that affect how they are built.
Here, our project is first_c, and properties are written as
PROPERTIES prop1 value1
Now run the commands
cmake . make
You will see our folder structure now as:
Hurray, we have our libhello.dylib now!!
Integrate C Library in Flutter MacOS
Now we need to integrate the C library in our Flutter Desktop application.
The steps needed are documented in this link, follow along with the article or the above video.
- Open the
yourapp/macos/Runner.xcworkspace
in Xcode - Drag your precompiled library (
libyourlibrary.dylib
) intoRunner/Frameworks
. - Click
Runner
and go to theBuild Phases
tab.
- Drag
libyourlibrary.dylib
into theCopy Bundle Resources
list. - Under
Bundle Framework
, checkCode Sign on Copy
. - Under,
Link Binary With Libraries
set status toOptional
.
4. Click Runner
and go to the General
tab.
- Drag
libyourlibrary.dylib
into theFrameworks, Libararies and Embedded Content
list. - Select
Embed & Sign
.
Your Flutter Desktop project structure should look like this
Call from Flutter MacOS
We will call our dynamic libraries from Flutter Desktop now.
- Import the dart ffi package(present inside Flutter) as
import 'dart:ffi' as ffi
This has a class DynamicLibrary. We call the method open and load our dynamic library (libhello.dylib).
- Lookup for the symbol from the loaded dynamic library using lookup or lookupFunction.
- Finally, call the function ‘hello_world’ (exposed from header file .h)
typedef hello_world_func = ffi.Void Function(); void openFromFlutter() { final sysLib = ffi.DynamicLibrary.open('libhello.dylib'); final HelloWorld hello = sysLib .lookup<ffi.NativeFunction<hello_world_func>>('hello_world') .asFunction(); hello(); // Call the function }
BONUS
Let’s say we have a function in the C header file like this
char *sayHello(char *str);
Since this accepts the input parameter and the return type as char pointer, we need to convert our Flutter Strings to/from Pointer.
- We specify SystemCHello and SystemDartHello functions, which basically are
typedef SystemCHello = ffi.Pointer<Utf8> Function(ffi.Pointer<Utf8> str); typedef SystemDartHello = ffi.Pointer<Utf8>Function(ffi.Pointer<Utf8> str);
- Convert our Flutter String to Utf8 and pass it to the function
final sysLib = ffi.DynamicLibrary.open('libfetchtemp.dylib'); final helloFromC = sysLib.lookupFunction<SystemCHello, SystemDartHello>('sayHello'); // Pass input final name = Utf8.toUtf8(input); // Call the function final res = helloFromC(name);
- Since the value returned from the function, is also a pointer, we convert it to Flutter String
// Convert response into string
final strRes = Utf8.fromUtf8(res);
Hosted URL : https://web.flatteredwithflutter.com/#/
Top comments (0)