Direct Native Interop Moves Into Pigeon 29, Giving Flutter a Synchronous FFI/JNI Path
Flutter plugin authors stand to lose the hand-written Kotlin and Swift bridge code in favor of generated bindings, gaining synchronous calls and reduced data copying. Pigeon 29's experimental support indicates the Flutter team is preparing to make direct interop the default for new plugins, which will force existing platform-channel-based plugins to rethink their threading and build setup.
Direct Native Interop has been evolving for two years, and with Pigeon 29 it finally lands inside Flutter's standard plugin toolchain. Dart 3.5 started improving C, Java, Kotlin, and Objective-C interop in 2024; Dart 3.8 announced FFIgen and JNIgen's early access in May 2025; now dart.dev/interop consolidates the C, Java, Objective-C/Swift, and JavaScript paths. The immediate effect: developers can generate Dart bindings from native interfaces and call them synchronously, bypassing the Platform Channel's message encoding and decoding.
Pigeon 29 adds experimental FFI/JNI support. Turning it on requires `useJni: true` for Kotlin and `useFfi: true` for Swift in the Pigeon configuration; `dart run pigeon` then invokes `jnigen`/`ffigen` to produce both bridge code and Dart bindings. The comparison table makes the trade-off clear: direct FFI/JNI runs on the caller's OS thread, supports sync calls, and reduces data copying, but code generation, lifecycle, and build configuration become more complex. Current direct support covers Android, iOS, and macOS, with Windows and Linux still on the traditional channel.
Swift remains the awkward case. Dart FFI targets the C ABI, so Swift classes still need an `@objc` shim to be callable. Separately, Dart 3.13 brought `@RecordUse` and `package:record_use`, letting native FFI bindings participate in AOT reachability analysis, so unused native symbols can be stripped from the final binary when combined with link hooks and a trimming-capable toolchain. That optimization only kicks in with proper build and link setup; simply upgrading Dart won't shrink every plugin.
The two-year timeline shows the Flutter team deliberately kept Direct Native Interop behind separate tooling until Pigeon 29, which is when it enters the standard plugin-generation workflow. That gap is why most developers still see the feature as new.
The trade-off table puts lifecycle and build complexity on the platform-channel side: synchronous calls and fewer copies arrive with the burden of managing generated bindings and native memory, which the old message-codec approach hid.
Swift's dependence on an `@objc` shim is a pragmatic concession to the C ABI, but it means pure Swift projects won't get first-class interop without an extra compatibility layer, slowing adoption in newer iOS codebases.
This Dart call will go directly to the Java VM via JNI. Doesn't that make it even slower... JNI also has overhead.