Switch to send port approach
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart
index 17be885..30bf0b4 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart
@@ -150,8 +150,11 @@
     final listenerTrampoline = w.topLevelUniqueNamer.makeUnique(
       '_${name}_listenerTrampoline',
     );
-    final listenerCallable = w.topLevelUniqueNamer.makeUnique(
-      '_${name}_listenerCallable',
+    final listenerPort = w.topLevelUniqueNamer.makeUnique(
+      '_${name}_listenerPort',
+    );
+    final listenerSendPort = w.topLevelUniqueNamer.makeUnique(
+      '_${name}_listenerSendPort',
     );
     final blockingTrampoline = w.topLevelUniqueNamer.makeUnique(
       '_${name}_blockingTrampoline',
@@ -210,19 +213,21 @@
       s.write('''
 $returnFfiDartType $sharedTrampoline(
     $blockCType block, ${func.paramsFfiDartType}) {
-  block.ref.trampoline.cast<${func.trampNatFnCType}>()
-      .asFunction<${func.trampFfiDartType}>()(${retains.join(', ')});
+  final msg = (${retains.join(', ')});
+  $listenerSendPort.send(msg);
 }
 ${func.trampNatCallType} $sharedCallable =
     ${func.trampNatCallType}.isolateGroupShared(
         $sharedTrampoline $exceptionalReturn)..keepIsolateAlive = false;
-$returnFfiDartType $listenerTrampoline(
-    $blockCType block, ${func.paramsFfiDartType}) {
+$returnFfiDartType $listenerTrampoline(dynamic msg) {
+  final ${params.isEmpty ? '$blockCType block' : '($blockCType block, ${func.paramsFfiDartType})'} = msg;
   ($getBlockClosure(block) as ${func.ffiDartType})(${func.paramsNameOnly});
   $releaseFn(block.cast());
 }
-${func.trampNatCallType} $listenerCallable = ${func.trampNatCallType}.listener(
-    $listenerTrampoline $exceptionalReturn)..keepIsolateAlive = false;
+final $listenerPort = RawReceivePort(
+    $listenerTrampoline)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final $listenerSendPort = $listenerPort.sendPort;
 $returnFfiDartType $blockingTrampoline(
     $blockCType block, ${blockingFunc.paramsFfiDartType}) {
   try {
@@ -332,8 +337,8 @@
   static $blockType listener(${func.dartType} fn,
           {bool keepIsolateAlive = true}) {
     final tramp = $newClosureBlock($sharedCallable.nativeFunction.cast(),
-        $listenerConvFn, keepIsolateAlive,
-        trampoline: $listenerCallable.nativeFunction.cast());
+        $listenerConvFn, keepIsolateAlive);
+    print('listener send port: ' + $listenerSendPort.toString());
     return $blockType(tramp, retain: true, release: true);
   }
 
diff --git a/pkgs/ffigen/lib/src/code_generator/writer.dart b/pkgs/ffigen/lib/src/code_generator/writer.dart
index c8a9716..af955eb 100644
--- a/pkgs/ffigen/lib/src/code_generator/writer.dart
+++ b/pkgs/ffigen/lib/src/code_generator/writer.dart
@@ -259,6 +259,7 @@
         ..writeln("@$ffiLibraryPrefix.DefaultAsset('$nativeAssetId')")
         ..writeln('library;\n');
     }
+    result.writeln("import 'dart:isolate';");
 
     /// Write [lookUpBindings].
     if (lookUpBindings.isNotEmpty) {
diff --git a/pkgs/ffigen/test/native_objc_test/block_bindings.dart b/pkgs/ffigen/test/native_objc_test/block_bindings.dart
index 0839b86..c245d4b 100644
--- a/pkgs/ffigen/test/native_objc_test/block_bindings.dart
+++ b/pkgs/ffigen/test/native_objc_test/block_bindings.dart
@@ -4,6 +4,7 @@
 //
 // Generated by `package:ffigen`.
 // ignore_for_file: type=lint
+import 'dart:isolate';
 import 'dart:ffi' as ffi;
 import 'package:objective_c/objective_c.dart' as objc;
 import 'package:ffi/ffi.dart' as pkg_ffi;
@@ -290,15 +291,8 @@
 void _ObjCBlock_ffiVoid_sharedTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl> block)
-        >
-      >()
-      .asFunction<void Function(ffi.Pointer<objc.ObjCBlockImpl>)>()(
-    objc.objectRetain(block.cast()).cast(),
-  );
+  final msg = (objc.objectRetain(block.cast()).cast());
+  _ObjCBlock_ffiVoid_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>)>
@@ -307,19 +301,18 @@
         ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>)
       >.isolateGroupShared(_ObjCBlock_ffiVoid_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-) {
+void _ObjCBlock_ffiVoid_listenerTrampoline(dynamic msg) {
+  final ffi.Pointer<objc.ObjCBlockImpl> block = msg;
   (objc.getBlockClosure(block) as void Function())();
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>)>
-_ObjCBlock_ffiVoid_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>)
-      >.listener(_ObjCBlock_ffiVoid_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_listenerSendPort =
+    _ObjCBlock_ffiVoid_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -420,7 +413,9 @@
       _ObjCBlock_ffiVoid_sharedCallable.nativeFunction.cast(),
       () => fn(),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_listenerCallable.nativeFunction.cast(),
+    );
+    print(
+      'listener send port: ' + _ObjCBlock_ffiVoid_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function()>(
       tramp,
@@ -1896,21 +1891,8 @@
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<objc.ObjCSelector> arg0,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            ffi.Pointer<objc.ObjCSelector> arg0,
-          )
-        >
-      >()
-      .asFunction<
-        void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCSelector>,
-        )
-      >()(objc.objectRetain(block.cast()).cast(), arg0);
+  final msg = (objc.objectRetain(block.cast()).cast(), arg0);
+  _ObjCBlock_ffiVoid_objcObjCSelector_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -1927,29 +1909,22 @@
         )
       >.isolateGroupShared(_ObjCBlock_ffiVoid_objcObjCSelector_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_objcObjCSelector_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  ffi.Pointer<objc.ObjCSelector> arg0,
-) {
+void _ObjCBlock_ffiVoid_objcObjCSelector_listenerTrampoline(dynamic msg) {
+  final (
+    ffi.Pointer<objc.ObjCBlockImpl> block,
+    ffi.Pointer<objc.ObjCSelector> arg0,
+  ) = msg;
   (objc.getBlockClosure(block)
       as void Function(ffi.Pointer<objc.ObjCSelector>))(arg0);
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(
-    ffi.Pointer<objc.ObjCBlockImpl>,
-    ffi.Pointer<objc.ObjCSelector>,
-  )
->
-_ObjCBlock_ffiVoid_objcObjCSelector_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCSelector>,
-        )
-      >.listener(_ObjCBlock_ffiVoid_objcObjCSelector_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_objcObjCSelector_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_objcObjCSelector_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_objcObjCSelector_listenerSendPort =
+    _ObjCBlock_ffiVoid_objcObjCSelector_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_objcObjCSelector_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -2072,9 +2047,10 @@
       _ObjCBlock_ffiVoid_objcObjCSelector_sharedCallable.nativeFunction.cast(),
       (ffi.Pointer<objc.ObjCSelector> arg0) => fn(arg0),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_objcObjCSelector_listenerCallable
-          .nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_objcObjCSelector_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function(ffi.Pointer<objc.ObjCSelector>)>(
       tramp,
@@ -2732,19 +2708,11 @@
   ffi.Pointer<objc.ObjCBlockImpl> block,
   IntBlock arg0,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            IntBlock arg0,
-          )
-        >
-      >()
-      .asFunction<void Function(ffi.Pointer<objc.ObjCBlockImpl>, IntBlock)>()(
+  final msg = (
     objc.objectRetain(block.cast()).cast(),
     objc.blockRetain(arg0.cast()).cast(),
   );
+  _ObjCBlock_ffiVoid_IntBlock_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, IntBlock)>
@@ -2753,20 +2721,18 @@
         ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, IntBlock)
       >.isolateGroupShared(_ObjCBlock_ffiVoid_IntBlock_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_IntBlock_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  IntBlock arg0,
-) {
+void _ObjCBlock_ffiVoid_IntBlock_listenerTrampoline(dynamic msg) {
+  final (ffi.Pointer<objc.ObjCBlockImpl> block, IntBlock arg0) = msg;
   (objc.getBlockClosure(block) as void Function(IntBlock))(arg0);
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, IntBlock)>
-_ObjCBlock_ffiVoid_IntBlock_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, IntBlock)
-      >.listener(_ObjCBlock_ffiVoid_IntBlock_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_IntBlock_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_IntBlock_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_IntBlock_listenerSendPort =
+    _ObjCBlock_ffiVoid_IntBlock_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_IntBlock_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -2907,8 +2873,10 @@
         ),
       ),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_IntBlock_listenerCallable.nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_IntBlock_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<
       ffi.Void Function(objc.ObjCBlock<ffi.Int32 Function(ffi.Int32)>)
@@ -3024,21 +2992,8 @@
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<objc.ObjCObject> arg0,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            ffi.Pointer<objc.ObjCObject> arg0,
-          )
-        >
-      >()
-      .asFunction<
-        void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >()(objc.objectRetain(block.cast()).cast(), objc.objectRetain(arg0));
+  final msg = (objc.objectRetain(block.cast()).cast(), objc.objectRetain(arg0));
+  _ObjCBlock_ffiVoid_DummyObject_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -3055,30 +3010,23 @@
         )
       >.isolateGroupShared(_ObjCBlock_ffiVoid_DummyObject_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_DummyObject_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  ffi.Pointer<objc.ObjCObject> arg0,
-) {
+void _ObjCBlock_ffiVoid_DummyObject_listenerTrampoline(dynamic msg) {
+  final (
+    ffi.Pointer<objc.ObjCBlockImpl> block,
+    ffi.Pointer<objc.ObjCObject> arg0,
+  ) = msg;
   (objc.getBlockClosure(block) as void Function(ffi.Pointer<objc.ObjCObject>))(
     arg0,
   );
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(
-    ffi.Pointer<objc.ObjCBlockImpl>,
-    ffi.Pointer<objc.ObjCObject>,
-  )
->
-_ObjCBlock_ffiVoid_DummyObject_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >.listener(_ObjCBlock_ffiVoid_DummyObject_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_DummyObject_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_DummyObject_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_DummyObject_listenerSendPort =
+    _ObjCBlock_ffiVoid_DummyObject_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_DummyObject_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -3199,8 +3147,10 @@
       (ffi.Pointer<objc.ObjCObject> arg0) =>
           fn(DummyObject.castFromPointer(arg0, retain: false, release: true)),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_DummyObject_listenerCallable.nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_DummyObject_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function(DummyObject)>(
       tramp,
@@ -3308,21 +3258,8 @@
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<objc.ObjCObject> arg0,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            ffi.Pointer<objc.ObjCObject> arg0,
-          )
-        >
-      >()
-      .asFunction<
-        void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >()(objc.objectRetain(block.cast()).cast(), objc.objectRetain(arg0));
+  final msg = (objc.objectRetain(block.cast()).cast(), objc.objectRetain(arg0));
+  _ObjCBlock_ffiVoid_DummyObject$1_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -3339,30 +3276,23 @@
         )
       >.isolateGroupShared(_ObjCBlock_ffiVoid_DummyObject$1_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_DummyObject$1_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  ffi.Pointer<objc.ObjCObject> arg0,
-) {
+void _ObjCBlock_ffiVoid_DummyObject$1_listenerTrampoline(dynamic msg) {
+  final (
+    ffi.Pointer<objc.ObjCBlockImpl> block,
+    ffi.Pointer<objc.ObjCObject> arg0,
+  ) = msg;
   (objc.getBlockClosure(block) as void Function(ffi.Pointer<objc.ObjCObject>))(
     arg0,
   );
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(
-    ffi.Pointer<objc.ObjCBlockImpl>,
-    ffi.Pointer<objc.ObjCObject>,
-  )
->
-_ObjCBlock_ffiVoid_DummyObject$1_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >.listener(_ObjCBlock_ffiVoid_DummyObject$1_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_DummyObject$1_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_DummyObject$1_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_DummyObject$1_listenerSendPort =
+    _ObjCBlock_ffiVoid_DummyObject$1_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_DummyObject$1_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -3489,9 +3419,10 @@
             : DummyObject.castFromPointer(arg0, retain: false, release: true),
       ),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_DummyObject$1_listenerCallable
-          .nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_DummyObject$1_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function(DummyObject?)>(
       tramp,
@@ -3630,30 +3561,13 @@
   Vec4 arg1,
   ffi.Pointer<objc.ObjCObject> arg2,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            Vec2 arg0,
-            Vec4 arg1,
-            ffi.Pointer<objc.ObjCObject> arg2,
-          )
-        >
-      >()
-      .asFunction<
-        void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          Vec2,
-          Vec4,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >()(
+  final msg = (
     objc.objectRetain(block.cast()).cast(),
     arg0,
     arg1,
     objc.objectRetain(arg2),
   );
+  _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -3676,12 +3590,13 @@
         _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_sharedTrampoline,
       )
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  Vec2 arg0,
-  Vec4 arg1,
-  ffi.Pointer<objc.ObjCObject> arg2,
-) {
+void _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerTrampoline(dynamic msg) {
+  final (
+    ffi.Pointer<objc.ObjCBlockImpl> block,
+    Vec2 arg0,
+    Vec4 arg1,
+    ffi.Pointer<objc.ObjCObject> arg2,
+  ) = msg;
   (objc.getBlockClosure(block)
       as void Function(Vec2, Vec4, ffi.Pointer<objc.ObjCObject>))(
     arg0,
@@ -3691,24 +3606,12 @@
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(
-    ffi.Pointer<objc.ObjCBlockImpl>,
-    Vec2,
-    Vec4,
-    ffi.Pointer<objc.ObjCObject>,
-  )
->
-_ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          Vec2,
-          Vec4,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >.listener(_ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerSendPort =
+    _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -3859,9 +3762,10 @@
         objc.NSObject.castFromPointer(arg2, retain: false, release: true),
       ),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerCallable
-          .nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_Vec2_Vec4_NSObject_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function(Vec2, Vec4, objc.NSObject)>(
       tramp,
@@ -3981,21 +3885,8 @@
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<objc.ObjCObject> arg0,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            ffi.Pointer<objc.ObjCObject> arg0,
-          )
-        >
-      >()
-      .asFunction<
-        void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >()(objc.objectRetain(block.cast()).cast(), objc.objectRetain(arg0));
+  final msg = (objc.objectRetain(block.cast()).cast(), objc.objectRetain(arg0));
+  _ObjCBlock_ffiVoid_NSString_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -4012,30 +3903,23 @@
         )
       >.isolateGroupShared(_ObjCBlock_ffiVoid_NSString_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_NSString_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  ffi.Pointer<objc.ObjCObject> arg0,
-) {
+void _ObjCBlock_ffiVoid_NSString_listenerTrampoline(dynamic msg) {
+  final (
+    ffi.Pointer<objc.ObjCBlockImpl> block,
+    ffi.Pointer<objc.ObjCObject> arg0,
+  ) = msg;
   (objc.getBlockClosure(block) as void Function(ffi.Pointer<objc.ObjCObject>))(
     arg0,
   );
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(
-    ffi.Pointer<objc.ObjCBlockImpl>,
-    ffi.Pointer<objc.ObjCObject>,
-  )
->
-_ObjCBlock_ffiVoid_NSString_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<objc.ObjCObject>,
-        )
-      >.listener(_ObjCBlock_ffiVoid_NSString_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_NSString_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_NSString_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_NSString_listenerSendPort =
+    _ObjCBlock_ffiVoid_NSString_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_NSString_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -4153,8 +4037,10 @@
       (ffi.Pointer<objc.ObjCObject> arg0) =>
           fn(objc.NSString.castFromPointer(arg0, retain: false, release: true)),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_NSString_listenerCallable.nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_NSString_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function(objc.NSString)>(
       tramp,
@@ -4283,25 +4169,8 @@
   Vec4 arg1,
   ffi.Pointer<ffi.Char> arg2,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            ffi.Int32 arg0,
-            Vec4 arg1,
-            ffi.Pointer<ffi.Char> arg2,
-          )
-        >
-      >()
-      .asFunction<
-        void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          int,
-          Vec4,
-          ffi.Pointer<ffi.Char>,
-        )
-      >()(objc.objectRetain(block.cast()).cast(), arg0, arg1, arg2);
+  final msg = (objc.objectRetain(block.cast()).cast(), arg0, arg1, arg2);
+  _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -4324,35 +4193,24 @@
         _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_sharedTrampoline,
       )
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  int arg0,
-  Vec4 arg1,
-  ffi.Pointer<ffi.Char> arg2,
-) {
+void _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerTrampoline(dynamic msg) {
+  final (
+    ffi.Pointer<objc.ObjCBlockImpl> block,
+    int arg0,
+    Vec4 arg1,
+    ffi.Pointer<ffi.Char> arg2,
+  ) = msg;
   (objc.getBlockClosure(block)
       as void Function(int, Vec4, ffi.Pointer<ffi.Char>))(arg0, arg1, arg2);
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(
-    ffi.Pointer<objc.ObjCBlockImpl>,
-    ffi.Int32,
-    Vec4,
-    ffi.Pointer<ffi.Char>,
-  )
->
-_ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Int32,
-          Vec4,
-          ffi.Pointer<ffi.Char>,
-        )
-      >.listener(_ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerSendPort =
+    _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -4500,9 +4358,10 @@
           .cast(),
       (int arg0, Vec4 arg1, ffi.Pointer<ffi.Char> arg2) => fn(arg0, arg1, arg2),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerCallable
-          .nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_Int32_Vec4_ffiChar_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<
       ffi.Void Function(ffi.Int32, Vec4, ffi.Pointer<ffi.Char>)
@@ -4617,18 +4476,8 @@
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Int32> arg0,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            ffi.Pointer<ffi.Int32> arg0,
-          )
-        >
-      >()
-      .asFunction<
-        void Function(ffi.Pointer<objc.ObjCBlockImpl>, ffi.Pointer<ffi.Int32>)
-      >()(objc.objectRetain(block.cast()).cast(), arg0);
+  final msg = (objc.objectRetain(block.cast()).cast(), arg0);
+  _ObjCBlock_ffiVoid_Int32_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -4642,25 +4491,19 @@
         )
       >.isolateGroupShared(_ObjCBlock_ffiVoid_Int32_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_Int32_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  ffi.Pointer<ffi.Int32> arg0,
-) {
+void _ObjCBlock_ffiVoid_Int32_listenerTrampoline(dynamic msg) {
+  final (ffi.Pointer<objc.ObjCBlockImpl> block, ffi.Pointer<ffi.Int32> arg0) =
+      msg;
   (objc.getBlockClosure(block) as void Function(ffi.Pointer<ffi.Int32>))(arg0);
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, ffi.Pointer<ffi.Int32>)
->
-_ObjCBlock_ffiVoid_Int32_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(
-          ffi.Pointer<objc.ObjCBlockImpl>,
-          ffi.Pointer<ffi.Int32>,
-        )
-      >.listener(_ObjCBlock_ffiVoid_Int32_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_Int32_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_Int32_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_Int32_listenerSendPort =
+    _ObjCBlock_ffiVoid_Int32_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_Int32_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -4779,8 +4622,10 @@
       _ObjCBlock_ffiVoid_Int32_sharedCallable.nativeFunction.cast(),
       (ffi.Pointer<ffi.Int32> arg0) => fn(arg0),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_Int32_listenerCallable.nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_Int32_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function(ffi.Pointer<ffi.Int32>)>(
       tramp,
@@ -4869,19 +4714,8 @@
   ffi.Pointer<objc.ObjCBlockImpl> block,
   int arg0,
 ) {
-  block.ref.trampoline
-      .cast<
-        ffi.NativeFunction<
-          ffi.Void Function(
-            ffi.Pointer<objc.ObjCBlockImpl> block,
-            ffi.Int32 arg0,
-          )
-        >
-      >()
-      .asFunction<void Function(ffi.Pointer<objc.ObjCBlockImpl>, int)>()(
-    objc.objectRetain(block.cast()).cast(),
-    arg0,
-  );
+  final msg = (objc.objectRetain(block.cast()).cast(), arg0);
+  _ObjCBlock_ffiVoid_Int32$1_listenerSendPort.send(msg);
 }
 
 ffi.NativeCallable<
@@ -4892,22 +4726,18 @@
         ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, ffi.Int32)
       >.isolateGroupShared(_ObjCBlock_ffiVoid_Int32$1_sharedTrampoline)
       ..keepIsolateAlive = false;
-void _ObjCBlock_ffiVoid_Int32$1_listenerTrampoline(
-  ffi.Pointer<objc.ObjCBlockImpl> block,
-  int arg0,
-) {
+void _ObjCBlock_ffiVoid_Int32$1_listenerTrampoline(dynamic msg) {
+  final (ffi.Pointer<objc.ObjCBlockImpl> block, int arg0) = msg;
   (objc.getBlockClosure(block) as void Function(int))(arg0);
   objc.objectRelease(block.cast());
 }
 
-ffi.NativeCallable<
-  ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, ffi.Int32)
->
-_ObjCBlock_ffiVoid_Int32$1_listenerCallable =
-    ffi.NativeCallable<
-        ffi.Void Function(ffi.Pointer<objc.ObjCBlockImpl>, ffi.Int32)
-      >.listener(_ObjCBlock_ffiVoid_Int32$1_listenerTrampoline)
-      ..keepIsolateAlive = false;
+final _ObjCBlock_ffiVoid_Int32$1_listenerPort = RawReceivePort(
+  _ObjCBlock_ffiVoid_Int32$1_listenerTrampoline,
+)..keepIsolateAlive = false;
+@pragma('vm:shared')
+final _ObjCBlock_ffiVoid_Int32$1_listenerSendPort =
+    _ObjCBlock_ffiVoid_Int32$1_listenerPort.sendPort;
 void _ObjCBlock_ffiVoid_Int32$1_blockingTrampoline(
   ffi.Pointer<objc.ObjCBlockImpl> block,
   ffi.Pointer<ffi.Void> waiter,
@@ -5019,8 +4849,10 @@
       _ObjCBlock_ffiVoid_Int32$1_sharedCallable.nativeFunction.cast(),
       (int arg0) => fn(arg0),
       keepIsolateAlive,
-      trampoline: _ObjCBlock_ffiVoid_Int32$1_listenerCallable.nativeFunction
-          .cast(),
+    );
+    print(
+      'listener send port: ' +
+          _ObjCBlock_ffiVoid_Int32$1_listenerSendPort.toString(),
     );
     return objc.ObjCBlock<ffi.Void Function(ffi.Int32)>(
       tramp,
diff --git a/pkgs/ffigen/test/native_objc_test/block_test.dart b/pkgs/ffigen/test/native_objc_test/block_test.dart
index 2cd343f..57f5a08 100644
--- a/pkgs/ffigen/test/native_objc_test/block_test.dart
+++ b/pkgs/ffigen/test/native_objc_test/block_test.dart
@@ -115,6 +115,7 @@
 
       await hasRun.future;
       expect(value, 123);
+      print('test done: $value');
     });
 
     /*test('Listener block new thread', () async {