Skip to content

Instantly share code, notes, and snippets.

@espresso3389
Last active April 5, 2024 11:30
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save espresso3389/be5674ab4e3154f0b7c43715dcef3d8d to your computer and use it in GitHub Desktop.
Save espresso3389/be5674ab4e3154f0b7c43715dcef3d8d to your computer and use it in GitHub Desktop.
Async messaging between Flutter and C++ using Dart ffi NativePort

C++

  • Copy Dart SDK (${FLUTTER_DIR}/bin/cache/dart-sdk/include) to your project, e.g. ios/Classes/
  • Add your own code like the following fragment
#include "dart_api_dl.h"

// Receives NativePort ID from Flutter code
static Dart_Port_DL dart_port = 0;

// Ensure that the function is not-mangled; exported as a pure C function
extern "C" void set_dart_port(Dart_Port_DL port)
{
    dart_port = port;
}

// Sample usage of Dart_PostCObject_DL to post message to Flutter side
void debug_print(const char *message)
{
    if (!dart_port)
        return;
    Dart_CObject msg;
    msg.type = Dart_CObject_kString;
    msg.value.as_string = (char *)message;
    // The function is thread-safe; you can call it anywhere on your C++ code
    Dart_PostCObject_DL(dart_port, &msg);
}

Flutter

import 'package:ffi/ffi.dart';

class FfiNativePort {
  // libffi_native_port.so is the shared module generated by the C++ code above
  static final module =
      Platform.isAndroid ? DynamicLibrary.open("libffi_native_port.so") : DynamicLibrary.process();

  // Dart_InitializeApiDL defined in Dart SDK (implemented in dart_api_dl.c)
  static final Dart_InitializeApiDLFunc _Dart_InitializeApiDL =
      module.lookup<NativeFunction<Dart_InitializeApiDLFunc>>("Dart_InitializeApiDL").asFunction();

  // The set_dart_port function defined on the C++ code above
  static final _SetDartPortFunc _setDartPort =
      module.lookup<NativeFunction<Void Function(Int64)>>("set_dart_port").asFunction();

  static Pointer<Void>? cookie;

  // initialization
  static void initialize() {
    if (cookie != null) {
      return;
    }

    // Call Dart_InitializeApiDL with NativeApi.initializeApiDLData
    cookie = _Dart_InitializeApiDL(NativeApi.initializeApiDLData);

     final pub = ReceivePort()
      ..listen((message) {
        // TODO: processing messages from C++ code
      });

    // Pass NativePort value (int) to C++ code
    _setDartPort(pub.sendPort.nativePort);
  }
}

Reference(s)

@cdigit
Copy link

cdigit commented Jan 24, 2023

Hi @espresso3389, Thanks for making this gist. I'm attempting to use it, but I'm flutter can't find these classes and types: Dart_InitializeApiDLFunc Dart_InitializeApiDLFunc _SetDartPortFunc. Is there more that needs to be imported outside of package:ffi/ffi.dart and dart:isolate ?

@espresso3389
Copy link
Author

espresso3389 commented Jan 24, 2023

flutter_mozjpeg is a use-case of the mechanism.

@cdigit
Copy link

cdigit commented Jan 24, 2023

Thanks. The missing types were in flutter_mozjpeg.

@cdigit
Copy link

cdigit commented Jan 29, 2023

@espresso3389 do you happen to know how to send more complex objects? For example a pointer to a struct? The idea below throws an error. What do I need to assign to msg.value.as_native_pointer to be able to pass a struct?

// a simple struct
typedef struct {
    float * floatPtr;
    int nbrItems;
}simpleStruct;

// declare a pointer to a simple struct
simpleStruct * simpleStructPtr;

// posting to dart
Dart_CObject msg;
msg.type = Dart_CObject_kNativePointer;

*/ error: Assigning to 'struct (unnamed struct from incompatible type 'simpleStruct 
msg.value.as_native_pointer  = (simpleStruct *) simpleStructPtr; 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment