Flutter + Kotlin Multiplatform, Write Once Run Anywhere

littlegnal
5 min readJul 10, 2019

--

Motivation

Flutter is a Google’s cross-platform framework launched in 2017, featuring Fast Development, Expressive and Flexible UI, Native Performance and more. Flutter uses Dart as the development language, Android and iOS projects can share the same Dart code. Many people can’t wait to try it, including me, but during the learning process, I am thinking about the following questions:

  • Flutter is excellent, but relatively new. At present, not all third-party SDKs support Flutter (especially in China), when using third-party SDKs, we often need to write native code integration logic, which requires us to write separate integration code for Android and iOS separately.
  • When we want to integrate Flutter into our project, it is a bit impractical to replace all the pages at once, but when we need some pages integration, we will need to use Dart for rewriting the common logic such as database operations logic. Because other pages also need using database operation logic, there is no way to keep only Dart’s implementation code. Thus, the same database operation logic will need to provide different frameworks implementations such as: Dao.kt, Dao.swift, Dao.dart. Of course, you can use the MethodChannel/ FlutterMethodChannel provided by Flutter to directly call the logic of the native code, but if the database operation logic needs to be modified, we still have to modify the code logic of the different frameworks at the same time.
  • If your team has an internal SDK that is available for different projects (Android and iOS), but some APPs need to integrate Flutter, the maintainer will need to provide the Flutter/Android/iOS SDK separately which increases the maintenance and implementation costs of the SDK maintainer.

Therefore, the problem can be attributed to the fact that native code cannot be reused, which leads us to implement the same code logic for different frameworks. Are there any frameworks for reusing native code? Definitely YES! Kotlin Multiplatform is a feature of Kotlin (currently experimental) with the goal of using Kotlin: Sharing code between platforms.

Hence, I have a bold idea to use both Flutter and Kotlin Multiplatform, although different languages (Dart/Kotlin) are used, different frameworks share the same code logic implementations. Write common logic using Kotlin Multiplatform, then use MethodChannel/ FlutterMethodChannel on Android/iOS for Flutter to call the common logic.

Let’s take an example of implementing common database logic and briefly describe how to achieve the goal of Write Once Run Anywhere using Flutter and Kotlin Multiplatform.

Kotlin Multiplatform

We use Sqldelight to implement common database logic, then serialize the query results into JSON strings via kotlinx.serialization and pass them to Flutter via MethodChannel/ FlutterMethodChannel.

The project structure of Flutter is shown as below:

The android directory is a Gradle project, refer to the official document Multiplatform Project: iOS and Android, we create a common module in the android directory to store the common logic code.

Gradle script

Implement AccountingRepository

Create the commonMain directory under the common module, and create the AccountingRepository class in the commonMain directory to encapsulate the database logic (you don't need to care about the code implementation details here, the logic here is simply querying the database results and then serializing them to JSON strings).

We have implemented the common database logic here, but for Android/iOS to call the database logic more simply, we simply encapsulate the call logic of MethodChannel#setMethodCallHandler/ FlutterMethodChannel#setMethodCallHandler:

Because of the Result object in MethodChannel#setMethodHandler is different from the FlutterResult object in FlutterMethodChannel#setMethodHandler, we define the result function in SqlDelightManager#methodCall as external processing in the form of callbacks.

Use SqlDelightManager on Android

In order to use SqlDelightManager in Android projects, refer to the official documentation Multiplatform Project: iOS and Android, we need to add the dependency of common module to the app module firstly:

Referring to the official document Writing custom platform-specific code, we implement the MethodChannel in the MainActivity and call the SqlDelightManager#methodCall function:

Use SqlDelightManager on iOS

Referring to the Multiplatform Project: iOS and Android. In order for the Xcode project to recognize the code of the common module, you need to add the frameworks generated by the common module to the Xcode project. I briefly summarize the following steps:

  • Run ./gradlew :common:build to generate the iOS frameworks
  • General -> Add Embedded Binaries
  • Build Setting -> Add Framework Search Paths
  • Build Phases -> Add Run Script

The only difference from the official documentation is that the path to store frameworks is different. Because the Flutter project structure puts the build path of the android project to the root directory, the path of the frameworks should be $(SRCROOT)/../build/xcode- frameworks. You can check it in android/build.gradle:

Afterward, you can call the Kotlin code of the common module in Swift. Referring to the official documentation, Writing custom platform-specific code, we implement the FlutterMethodChannel in AppDelegate.swift and call the SqlDelightManager#methodCall function:

As you can see, except for the MethodChannel/ FlutterMethodChannel object and the Kotlin/Swift syntax, we are calling the same SqlDelightManager#methodCall function and don't need to implement the same logic on Android/iOS.

Finally, we have used Kotlin Multiplatform to reuse the native code. The only thing is to use MethodChannel to call the corresponding method in Flutter.

Flutter

Similarly, we implement the AccountingRepository class to encapsulate database logic in Flutter:

Simply use BLoC to call the AccountingRepository functions:

Use BLoC in Widgets:

DONE! Let’s take a look at what the APP looks like:

Android / iOS

TL;DR

This article briefly demonstrates how to use both Flutter and Kotlin Multiplatform to achieve Write Once Run Anywhere. As far as I am concerned, Kotlin Multiplatform has a good prospect. Not only Google released the next generation UI development framework Jetpack Compose on Google IO 2019, but Apple also brought us SwiftUI on WWDC 2019, which means that if someone unifies the APIs of these two frameworks, we can use Kotlin to write cross-platform code with native performance. The Demo of this article has been uploaded to GitHub, you can clone and study it if you are interested (although the code is very poor). Feel free to raise the issue if you have any questions. Have Fun!

Thank you so much for reading this article. I’m so sorry for my poor English, but I hope you can understand what I want to express.

--

--