Using React Native's Plugins with Flutter (Android version)

Flutter allows you to build beautiful native apps on iOS and Android from a single codebase. Like most cross-platform mobile application development frameworks, platform specific APIs and device capabilities are exposed to the developer environments using plugins.
React Native, another framework to build mobile apps using JavaScript and React, also has a similar concept, called Native Modules.

Native SDKs for Hybrid Frameworks

React Native has been around for a while and in addition to the numerous packages created by the community, companies also expose their services (think Square, AWS, OneSignal, MapBox, UrbanAirShip) to React Native apps using native modules. However, many services are only available as iOS and Android SDKs; developers building on the newer hybrid frameworks usually are left to create their own adapters.
I ran into this problem a few years ago when I was porting my Cordova app to the still new React Native framework. While all the Cordova plugins in my app were well supported first-party or community modules, none of them had existed for React Native back then. Instead of re-implementing every module, I explored the idea of creating an adapter to use Cordova Modules in a React Native application.
The share of Android and iOS apps are still significantly higher and creating an adapter for every hybrid framework like Cordova, Xamarin, NativeScript, React Native and Flutter may be too much work. Having been a Cordova committer, worked in a team adjacent to Xamarin, studied NativeScript and now contributing to React Native, I believe that the patterns for invoking native modules in all these hybrid frameworks may be similar enough to be able to create a "universal" system. By simply defining a "cross-platform" API and using a system of adapters, SDKs may be able to support all hybrid frameworks well.

React Native to Flutter

The promise of write-once-run-everywhere has historically been perilous, and it may would help to take smaller baby steps. As a start, I tried the approach to use native modules from React Native in my Flutter app; a port that was surprisingly simple.
Plugins can be bootstrapped using Flutter's CLI that generates both the dart file and the corresponding Java code. The onMethodCall function in Java has an if-else condition that is responsible for executing the right method that the application code in dart invokes.
In React Native, methods exposed to JavaScript are annotated with @ReactMethod. When the native modules is "required" in JavaScript, the methods on the module are discovered using reflection. We can use the same method to use React Native modules in Flutter. 

For the demo, we use the ToastModule that is also used as an example in the React Native documentation. We lazily create a map of string method names to the actual implementation and invoke it when dart calls the method.
 The only challenge is that most of the native modules refer to other React Native bridge classes, many of which we would have to import or stub out. In this case, we simply had to stub out most of the classes.
Additionally, in production, we would not discover the methods at runtime, but would instead generate the onMethodCall if-else at compile time to call the appropriate React Native methods.

The Universal Modules System


While the above method is not perfect, it does get existing React Native modules to work with Flutter. Ideally, we would not need the reflection and instead just have a "universal" interface that can generate plugins for Flutter, React Native and other hybrid systems.
Service SDKs could simply define something like a Typescript file containing the individual method, their arguments and return types that map to the iOS and Android SDKs and the entire plugin code could be generated. Note that TurboModules will use this code generation approach, with the source of truth for the API being in JavaScript. This method would also eliminate the React Native specific code present in all the dependencies.

As I was working on this, Stanisław Chmiela pointed out the work that Expo has been doing in this area. The video does talk of a "Swagger like" API definition for the interfaces. It works with React Native, with an unreleased implementation for Flutter. Extending it to Cordova or NativeScript should not be very hard either.

Though this is still pretty early, I believe that this idea of a universal module system does hold promise and could help ensure that native modules in all the hybrid frameworks are well maintained, and get the same amount of attention that native iOS or Android SDKs get.