There have been indications in recent times of changes being made that would allow use of Jetpack Compose to develop UI for desktop apps. That became closer to reality today when it was noticed that EAP version of IntelliJ IDE has option now to create projects that target use of Compose on desktop JVM platforms (Windows, Linux, macOS). In this article I’m going to outline initial exploration of using this capability in PeopleInSpace repo. Changes made are included in this branch.

Update: Since writing this article the Compose for Desktop code has been integrated in to main branch for both PeopleInSpace and BikeShare projects and can now be opened/run using Android Studio (Canary).

Intellij Desktop Compose

(Update Nov 5th 2020: Official announcement for Jetpack Compose for Desktop: Milestone 1 Released)

The existing PeopleInSpace project currently has dependency on Android Studio 4.2 Canary version so for purposes of this experiement I removed the app module (and related Android code) from the project and created a new compose module for the desktop Compose code. It was then mostly a matter of copying in main.kt and build.gradle.kts from the sample IntelliJ project in to this module. That code was then updated to include following (with PersonList and other Compose code being almost identical to that already used in Android app.). Note also that the same Kotlin Multiplatform shared code that was invoked from Android Jetpack Compose code (and also other platforms) was used here to retrieve data shown in the UI.

fun main() = Window {
    var peopleState by remember { mutableStateOf(emptyList<Assignment>()) }
    val peopleInSpaceApi = PeopleInSpaceApi()

    launchInComposition {
        peopleState = peopleInSpaceApi.fetchPeople().people
    }

    MaterialTheme {
        PersonList(peopleState) { }
    }
}

@Composable
fun PersonList(people: List<Assignment>, personSelected : (person : Assignment) -> Unit) {

    Scaffold(
        topBar = {
            TopAppBar(title = { Text("People In Space") })
        },
        bodyContent = {
            LazyColumnFor(items = people, itemContent = { person ->
                PersonView(person, personSelected)
            })
        }
    )
}


With those changes it was then just a matter of running main() (in main.kt) resulting then in following UI. One other important thing to note here is that you need to udpate IntelliJ run configuration to use bundled JRE (it has also been pointed out that selecting Java 11 should also work.)

Desktop Compose Screenshot

Running from command line

To allow running from command line (using java -jar) I’ve also updated build.gradle.kts for compose module to include following

tasks.withType<Jar> {
    manifest {
        attributes["Main-Class"] = "MainKt"
    }
}

tasks.register<Jar>("uberJar") {
    archiveClassifier.set("uber")

    from(sourceSets.main.get().output)

    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
    })
}

Having done that you can now run ./gradlew :compose:uberJar to create the jar file and then following to run from command line

java -jar ./compose/build/libs/compose-1.0-SNAPSHOT-uber.jar

(Update Nov 5th 2020)

With official Milestone release it turns out that Compose gradle plugin supports creating installers/packages for the supported platforms:

  • macOS — .dmg, .pkg
  • Windows — .exe, .msi
  • Linux — .deb, .rpm

For example:

compose.desktop {
    application {
        mainClass = "example.MainKt"

        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
        }
    }
}


Also shout out to Adam Bennett for following tweet (and related thread) for inspiring this experiment and also importantly highlighting some of steps required.


Featured in Kotlin Weekly Issue #223 and Android Weekly Issue #439