In this tutorial, you’ll learn how to use Dart FFI to access native libraries that support C-interoperability.
By Nick Fisher.
Sign up/Sign in
With a
free
Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Create account
Already a member of Kodeco?
Sign in
Sign up/Sign in
With a
free
Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Create account
Already a member of Kodeco?
Sign in
Binding Functions That Return Pointers
The double your C function returns is a stack-allocated primitive that freely passes between C and Dart code without any memory concerns.
What if you want to obtain a Dart
String
from your C function? The C standard library has no concept of a string, so you can only work with NULL-terminated
char
array pointers.
Note
: If you need a refresher on scope and memory allocation in C, you might want to read these
lecture slides.
Proper Scope
You might feel tempted to add the following to
weather.c
, but don’t:
char* get_forecast() {
char* forecast = "sunny";
return forecast;
This code will compile unless you’ve turned on the right compiler warnings, but it isn’t valid C!
Why? Because you created a stack-allocated
char
array, which is only valid within the scope of this function. Once you return the pointer to your Dart code, this no longer points to a valid
char
array in memory.
To safely return a
char
pointer, you
must
return a pointer to properly allocated memory.
Add the following code to
weather.c
:
char* get_forecast() {
char* forecast = "sunny";
char* forecast_m = malloc(strlen(forecast));
strcpy(forecast_m, forecast);
return forecast_m;
This function creates a local char pointer for the string
"sunny"
, allocates memory for a char pointer memory on the heap of the same size, and copies the contents of the former into the latter.
Then add the following
C library
include
s to the top of
weather.c
:
#include <string.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
This code includes header files from the C standard library so that you can use
strcpy
,
strlen
and a few other functions and types you’ll see later in this tutorial.
In this tutorial, you know exactly what memory you’re copying, so there’s no need to worry.
Note
: In general, you should avoid
strcpy
and
strlen
because you can inadvertently introduce security holes by copying untrusted data.
In this tutorial, you know exactly what memory you’re copying, so there’s no need to worry.
If you’re a seasoned C developer, you already know that returning pointers to locally-scoped variables is a big no-no. At best, it’ll create incorrect return values. At worst, your app will segfault and crash.
If this is your first time working with C, it’s worth reiterating:
don’t return pointers to locally-scoped variables!
Next, you need to create a Dart type to represent a function that accepts no arguments with a return value of type
Pointer
.
Typing Dart Functions That Return Pointers
On the Dart side, create the matching
typedefs
in
ffi_bridge.dart
. Locate
// TODO: Add new typedef declarations here
and replace it with:
typedef ForecastFunction = Pointer<Utf8> Function();
typedef ForecastFunctionDart = Pointer<Utf8> Function();
Dart FFI uses
Pointer<Utf8>
to represent a
char
pointer. Keep in mind, the Dart
typedef
doesn’t return a
String
because you need to manually free the returned pointer. In contrast,
TemperatureFunction
directly returns a Dart
double
.
Add Functions and Their Respective Lookups
Find and replace
// TODO: Add _getForecast declaration here
with:
ForecastFunctionDart _getForecast;
Here you added a
_getForecast
of type
ForecastFunctionDart
to your
FFIBridge
, which is the Dart function that acts as a bridge to a C function.
Next, replace
// TODO: Assign value to _getForecast
with:
_getForecast = dl
.lookupFunction<ForecastFunction, ForecastFunctionDart>('get_forecast');
This uses
DynamicLibrary
to locate the C function you’ll bridge to, using the name
get_forecast
and the typedef
ForecastFunction
.
Find
// TODO: Add getForecast() here
and replace it with:
String getForecast() {
final ptr = _getForecast();
final forecast = ptr.toDartString();
calloc.free(ptr);
return forecast;
In this code, you create a
getForecast
that invokes the bound Dart function, converts the returned char pointer to a Dart string and frees the memory allocated for the returned pointer.
Return to
main.dart
and locate
// TODO: Add code to invoke newly created forecast method
replace it and
throw UnimplementedError();
with:
_show(_ffiBridge.getForecast());
This invokes the
getForecast()
you added in the last step.
Build and run. Then click
Today’s forecast
.
Voila! Here you:
Obtain a native
char
pointer.
Convert the pointer to a UTF8/Dart
String
.
Free the allocated memory.
Pass the String back to your Flutter widget.
Both the
get_temperature
and
get_forecast
returned primitive types, a
double
, and a
char
pointer respectively. Neither of these functions accepted any arguments.
Next, you’ll see how to invoke a C function that accepts some arguments. You’ll also see how to return a more complicated data structure, not just a simple pointer.
Arguments and Structs
In this section, you’ll see how to pass arguments from Dart to C by creating a function to return a three-day forecast in either Celsius or Fahrenheit.
Creating A Three-Day Forecast Structure
A three-day forecast needs three temperature values, so you obviously can’t return a solitary
double
. You need to create an appropriate
struct
.
Add the following to the bottom of
weather.c
:
struct ThreeDayForecast {
double today;
double tomorrow;
double day_after;
Then add a function that converts between Fahrenheit and Celsius:
double fahrenheit_to_celsius(double temperature) {
return (5.0f / 9.0f) * (temperature - 32);
In the next section, you’ll write the function to create an instance of the
ThreeDayForecast
struct. This function will then populate the struct’s values with the temperature forecast in either Celsius or Fahrenheit.
Accepting Arguments And Returning Structs
Before your app can provide a three-day forecast in both Fahrenheit and Celsius, you need to create a function that accepts arguments.
At the bottom of
weather.c
, add:
struct ThreeDayForecast get_three_day_forecast(bool useCelsius) {
struct ThreeDayForecast forecast;
forecast.today = 87.0f;
forecast.tomorrow = 88.0f;
forecast.day_after = 89.0f;
if(useCelsius) {
forecast.today = fahrenheit_to_celsius(forecast.today);
forecast.tomorrow = fahrenheit_to_celsius(forecast.tomorrow);
forecast.day_after = fahrenheit_to_celsius(forecast.day_after);
return forecast;
Going through step-by-step, this function:
Accepts a
bool
indicating whether to return Celsius or Fahrenheit values.
Instantiates a struct with some very boring and static values, representing the forecasted temperature over the next three days.
Converts these values to Celsius if
useCelsius
is true.
Returns the struct.
Since this function returns a struct, you can’t use the same approach as
getForecast()
and return a
Pointer
. You need to create a matching class on the Dart side to receive the values in this struct.
Sign up/Sign in
With a
free
Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Create account
Already a member of Kodeco?
Sign in
Sign up/Sign in
With a
free
Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Create account
Already a member of Kodeco?
Sign in
This content was released on Jun 22 2021. The official support period is 6-months
from this date.
In this tutorial, you’ll learn how to use Dart FFI to access native libraries that support C-interoperability.
Sign up/Sign in
With a
free
Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Create account
Already a member of Kodeco?
Sign in
A Kodeco subscription is the best way to learn and master mobile development. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more
Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive
catalogue of 50+ books and 4,000+ videos.
The largest and most up-to-date collection of courses and books on iOS,
Swift, Android, Kotlin, Flutter, Dart, Server-Side Swift, Unity, and more!