Kotlin Multiplatform — How To Create an App for Android, Desktop, and Web With Kotlin in One Code Base | by Florian Curinga | Apr, 2022

JetBrains’ Compose Multiplatform reached Beta final yr, bringing their ambition to supply a cross-platform UI framework nearer

Kotlin Multiplatform general precept (supply: kotlinlang.org)

If you happen to have no idea what Compose Multiplatform is but, here’s a TLDR copied instantly from JetBrains’ website:

Quick reactive Desktop and Internet UI framework for Kotlin, primarily based on Google’s modern toolkit and dropped at you by JetBrains.

When mixed with Jetpack Compose, this implies we now have a technique to make an app focusing on cell, desktop, and net utilizing solely Kotlin.

You imply like Flutter however with Kotlin?

There are fairly notable variations between Kotlin Multiplatform (KMP) and Flutter, moreover the entire Dart vs Kotlin factor. The one I discover most fascinating is how they deal with their cross-platform structure.

In Flutter, you write your corporation logic, your UI, and it could possibly run wherever. You possibly can nonetheless write some adaptation logic to adapt the UI to the platform however on the finish of the day, whether or not you construct it right into a cell, desktop, or net app, your app is rendered by way of the Flutter engine (constructed on the Skia graphics library). That is nice for portability however doesn’t simply present a local UI expertise.

In KMP, you write the enterprise logic as soon as and put it in a shared library, then write your UI utilizing native libraries for every platform you goal, akin to Jetpack Compose for Android, Swift for iOS, Compose Multiplatform for desktop, and many others. Due to this fact your app finally ends up having a extra native feeling — at the price of platform adaptation for the UI.

The underside line is that these two present totally different approaches and which one you select will depend on your use case.

Truly, KMP sounds nice, signal me up!

Nicely, after spending fairly a while experimenting with it, I perceive and share your enthusiasm! However let’s put the speculation apart for now and concentrate on the “how.”

Nicely, we have to determine on what to develop so I can present you all of that in motion. Allow us to take inspiration from the Flutter “Good day World” app and make a counter app that may enable the consumer to increment and decrement a numerical worth, and in addition file what the most recent registered motion was.

Seems to be easy sufficient!

So what structure can we use now?

Structure

We are going to use Clear Structure with Mannequin-View-ViewModel (MVVM), which is a typical resolution for constructing GUI functions (particularly on Android). For the remainder of the tutorial, I’ll assume you might be conversant in this. If you’re not, you could depart this web page and read this earlier than. Sure, you’ve gotten my permission! 🙂

All proper let’s get began then. That is how we are going to construction our app:

  • Area: this may include the fashions for our app, on this case merely an information class for our counter (worth and final motion message)
  • Knowledge: our summary knowledge supply to retailer this counter
  • Use circumstances: the entire makes use of circumstances courses for our app, specifically: increment the counter, decrement it and get its worth
  • Presentation: the view mannequin for this counter view
  • Framework: knowledge supply implementation in addition to the UI for every platform

Each factor above might be shared throughout all platforms, apart from the UI, which is a part of “Framework.”

Our totally different modules

All proper, let’s dig into the code now. I’m utilizing IntelliJ IDEA Neighborhood, so hold that in thoughts. However I belief you as you adapt it to your favourite IDE as we go.

We must always begin by creating a brand new undertaking with the undertaking construction as detailed above, then create every class one after the opposite till we cowl all platforms. IDEA gives premade templates for KMP functions that we might use, however let’s do it from scratch for studying functions.

Open IDEA, then navigate to “File > New Venture.” Choose “Gradle,” then on the appropriate panel, depart every little thing empty apart from “Kotlin DSL construct script.” After this, we get Kotlin DSL because the construct script as a substitute of Gradle.

Create a brand new undertaking, I’ll identify mine “KmpDemo”.

Then identify your undertaking and create it.

Modules

Step one is to create the totally different modules: frequent, android, desktop, and net. We begin with frequent since all different modules will rely on it.

Proper-click on the undertaking root then “New > Module…”. You will note a well-recognized dialog field. This time, ensure to verify “Kotlin/Multiplatform” along with the others.

New module for frequent

Make sure that to call it “frequent.” You’ll get a construct sync error after creation:

Please initialize not less than one Kotlin goal in ‘frequent (:frequent)’.

Ignore it for now; we are going to repair it later.

Utilizing the identical technique, create the desktop, android and net modules.

Create a brand new file below the foundation, and identify it gradle.properties with the next content material:

kotlin.code.model=official
android.useAndroidX=true
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false
android.enableJetifier=true

The undertaking construction ought to now appear like this:

Our top-level modules

We nonetheless have some work to do to finalize the undertaking construction; we should always create the supply units.

Supply units

A KMP undertaking usually organizes its supply recordsdata in several units relying on their goal nature: frequent, JVM, and JS. We are going to comply with the really helpful construction detailed in the official docs.

Create the next directories below frequent:

  • src/androidMain/kotlin
  • src/androidTest/kotlin
  • src/commonMain/kotlin
  • src/commonTest/kotlin
  • src/desktopMain/kotlin
  • src/desktopTest/kotlin

Beneath desktop:

  • src/jvmMain/kotlin
  • src/jvmTest/kotlin

Beneath net:

  • src/jsMain/kotlin
  • src/jsTest/kotlin

And below android:

Wait, however I believed the frequent module was going to be platform-independent? Why do now we have these desktopMain and androidMain issues there?

Good query!

Allow us to take into account this (fictional) use case: you might be creating a multi-platform app, however you must entry some platform-specific APIs as properly. This can be retrieving some OS model, connecting to some logging system, or producing a random UUID.

Sometimes, you’ll leverage Dependency Inversion to create an interface within the frequent module and its implementations within the platform-specific ones. Nonetheless, this could result in some boilerplate code when the performance could be very easy, akin to producing an UUID.

KMP permits for a less complicated technique to obtain this utilizing the expect/actual mechanism and the construction above. You possibly can due to this fact do one thing like the next:

// Beneath Frequent
count on enjoyable randomUUID(): String
// Beneath Android
import java.util.*

precise enjoyable randomUUID() = UUID.randomUUID().toString()

// And so forth for all different platforms

This won’t be used for our Counter app however we higher be prepared for the long run. 🙂

Going again to our app, you must now have the next construction:

Don’t fear the flamboyant colours will seem quickly

Time to implement the construct scripts!

Construct scripts

Every module plus the foundation incorporates a construct script named construct.gradle.kts, and typically settings named settings.gradle.kts. These recordsdata outline how our undertaking is constructed: modules, dependencies, frameworks, and many others.

In settings.gradle.kts, write:

This merely defines which repositories are for use to fetch exterior dependencies in addition to the undertaking modules.

In construct.gradle.kts, write:

Extra of the identical, however this time we do it for the construct script and all modules.

In frequent/construct.gradle.kts, write:

Various issues right here. Let’s unpack this.

First, the plugins definition specifying the module is a Compose module and an Android library. Then, we specify the JS and JVM targets with some choices. That is required to permit the opposite modules to import frequent. The choices are the default ones.

We then outline the supply units, i.e. the place the Kotlin supply recordsdata are positioned, and what’s their kind and dependencies. Notice that commonMain will depend on kotlinx-coroutines-core and kodein-di-framework-compose. Lastly, we outline Android goal choices in a fashion that must be acquainted when you have labored with Android earlier than.

As you may see, we outline a manifest for Android, so let’s add it to the talked about path in frequent/src/androidMain/AndroidManifest.xml:

<?xml model="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" bundle="org.instance.kmpdemo"/>

Now, allow us to handle desktop/construct.gradle.kts:

Extra of the identical, however be aware that we explicitly outline the dependency on frequent.

Internet time! Write in net/construct.gradle.kts:

Extra of the identical, however as soon as once more, be aware that we explicitly outline the dependency on frequent.

And now for android/construct.gradle.kts:

We’re completed with the construct scripts, allow us to transfer on to the applying supply code now!

Frequent supply code

Beneath the commonMain/kotlin listing, create a bundle org.instance.kmpdemo.

On this bundle, create a site bundle with a file Counter.kt:

bundle org.instance.kmpdemo.area

/**
* Mannequin for the quantity counter
* @param worth The counter worth
* @param message Extra context hooked up to the counter
*/
knowledge class Counter (
val worth: Int = 0,
val message: String = "Init"
)

That is our mannequin for our counter. Easy sufficient!

Now in org.instance.kmpdemo, create a knowledge bundle with the recordsdata:

CounterDataSource.kt

CounterRepository.kt

Notice that we are going to use Flows to watch the counter values and that the strategies to increment and decrement are outlined as droop for synchronization flexibility.

Now, allow us to handle the use circumstances. In org.instance.kmpdemo, create a usecase bundle with the recordsdata:

GetCounter.kt

DecrementCounter.kt

IncrementCounter.kt

Now, let’s handle the view mannequin. In org.instance.kmpdemo, create a presentation bundle with the file CounterViewModel.kt:

Notice that the increment and decrement operations are carried out in a coroutine.

Virtually there! In org.instance.kmpdemo, create a framework bundle with the file InMemoryCounterDataSource.kt:

That is our precise (fundamental) implementation of our knowledge supply, our counter is a singleton saved on this class. Which means its worth will likely be reset each time the app restarts, however that’s adequate for now!

Time for Dependency Injection (DI). Create a file di/ServicesModule.kt below org.instance.kmpdemo.

Which means the view mannequin will now be injectable in our app view on all platforms. Neat!

We are actually completed with the frequent module!

Desktop Supply Code

Let’s implement the desktop app. I’ve solely examined this on Linux, however this could work on Home windows and Mac in the identical manner. Create a file, desktop/src/jvmMain/kotlin/Primary.kt:

Because of our dependency on frequent; we will merely retrieve our DI module and inject the view mannequin into our app. The counter Circulation is transformed to a State that performs properly with Compose, and the remaining is kind of easy.

Click on the run icon subsequent to the principle operate, and this must be your reward:

And clicking the buttons works, yay!

Create the bundle org.instance.kmpdemo.android with MainActivity.kt in it:

Feeling acquainted? Nicely time to run it:

Android: verify!

Internet supply code

First, we should always create the bottom index.html file, at net/src/jsMain/sources/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Counter app</title>
</head>
<physique>
<div id="root"></div>
<script src="net.js"></script>
</physique>
</html>

Now, create the net/src/jsMain/kotlin/Primary.kt file:

Click on the run icon subsequent to the principle operate, and this must be your reward:

Internet app. As you may see, CSS is my ardour…

Wait, this appears to be like very totally different. Additionally div, H1… am I writing HTML?!

Compose for Internet is sadly barely behind the opposite platforms in that regard:

“Compose for Internet at the moment doesn’t mean you can instantly reuse present widgets (not like the Android and Desktop targets for Jetpack Compose, which permit direct code sharing for many widgets out of the field).” Source

Nonetheless, the DOM API is practical and has the good thing about being fairly predictable in relation to the generated HTML and CSS.

Thanks for following me till the tip!

Hopefully, this small instance gave you additional insights about KMP and the need to attempt it on this context. The complete code for this undertaking is offered on this GitHub repo. Be happy to experiment with it and attain out for questions and suggestions!

More Posts