Hybrid Development Architecture for Flutter
Since the 2019 GDD, Flutter has become a rising star in mobile development. Thanks to its unique design, it far outpaces React Native, Weex, and other cross-platform frameworks in both performance and developer experience. However, migrating from an existing native tech stack to Flutter has a nontrivial cost, so teams typically won’t rewrite their entire native architecture – instead, they’ll pilot Flutter with non-core features first.
When Flutter is used alongside native tech stacks in hybrid development, cross-language communication becomes a major challenge. Flutter uses Dart (a language I evaluated in 2013 while doing technology selection for a Cloud IDE at SAMSUNG). Originally designed for the web as a replacement for JavaScript, Dart has since absorbed features from Java, JavaScript, and other popular languages. But interoperability between languages has always been a sore point.
To address cross-language invocation, Dart provides FFI (Foreign Function Interface) support, allowing Dart to call C/C++ APIs directly.
On the two major mobile platforms, iOS‘s Objective-C is a superset of C, so cross-language communication comes naturally. But Android‘s development languages are Java/Kotlin, and achieving communication between Dart and Java/Kotlin requires some detours.
Language Specification
Since Flutter hybrid development involves cross-language calls between Dart and Java/Kotlin as well as Objective-C/Swift, we need a more abstract language to express the communication protocol. IDL (Interface Description Language) was designed precisely for this purpose. Here’s a simple IDL example:
1 | interface Greeting { |
Specification Implementation
With IDL defining the interfaces, we still need implementations in each target language. Dart supports two communication mechanisms:
-
Since FFI only provides communication between Dart and C/C++, calling Java APIs requires JNI as a bridge:
-
The Platform Channels based implementation is similar to FFI:
Compiler
IDL (Interface Description Language) is merely a specification description without implementation. To actually invoke the Greeting interface above, you need implementations in the target languages. For example, to call Greeting from Java, you need a Java interface definition like this:
1 | interface Greeting { |
To call the Greeting interface from Dart, you need a Dart interface definition like this:
1 | abstract class Greeting { |
Therefore, a compiler is needed to generate target-language APIs from the IDL. For a more convenient Flutter hybrid development experience in an IDE, you’d also need IDE plugin support. For IntelliJ IDEA and Android Studio, that means developing an IDEA plugin to compile IDL files.
If we base the cross-language communication on Platform Channels, how should the compiler generate the implementations? Using the IDL example above, the generated implementations would look roughly like this:
Greeting.java
1 | interface Greeting { |
Greeting.dart
1 | abstract class Greeting { |
GreetingStub.dart
1 | abstract class GreetingStub implements Greeting { |
On the Java side, you can call Dart methods like this:
GreetingService.java
1 | class GreetingService { |
On the Dart side, you can respond to calls from the Java side like this:
1 | GreetingStub stub = new GreetingStub(channel) { |
- Blog Link: https://johnsonlee.io/2020/02/23/idl-for-flutter.en/
- Copyright Declaration: 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
