Compose Multiplatform Goes Beta: Stabilized APIs, Compatibility with Google’s Compose Artifacts, and More

Compose Multiplatform, the declarative UI framework for Kotlin, has reached Beta. This brings Compose for Desktop and Compose for Web another step closer to their stable release later this year. Here are the highlights:

In this blog post, we will cover all the details about the major changes with this release.

Stabilizing APIs

As we are moving towards the first stable release of Compose Multiplatform, we are proceeding with the stabilization of its APIs. Starting with this release, APIs which we believe might change in the future are annotated as experimental. Going forward, you can consider APIs that are not explicitly marked as experimental to be mostly stable.

As an example of an interface that could still change before 1.0, we can take mouseScrollFilter: it’s an API for managing scroll events, currently only available in Compose for Desktop. We want to make it available for all platforms – but such a commonization could obviously lead to some changes in its interface. Another example is mouseMoveFilter – we haven’t received enough feedback on its usage yet to fully determine whether its current form will also be its final form. As we gather more input from users, we may still consider applying some changes.

For a comprehensive list of all the API changes in this release, feel free to refer to the changelog in the official Compose Multiplatform repository. In this blog post, we’ll just be looking at the highlights.

Compatibility with Google’s Compose artifacts

Previously, Compose Multiplatform contained artifacts for Desktop, Web and Android. This caused issues when the framework was used simultaneously with Compose Android artifacts published by Google (e.g. when one framework was used together with a library based on another framework).

With Compose Multiplatform Beta, we have stopped publishing Android artifacts ourselves. Instead, Compose Multiplatform now refers to Google’s artifacts directly. This approach prevents typical issues, such as duplicate class conflicts. Developers will be able to use Compose without thinking about the artifact publisher, and library authors will be able to support Compose Multiplatform and Android-only Jetpack Compose use cases without having to deal with compatibility issues.

What’s new in Compose for Desktop

In the Beta release of Compose Multiplatform, the desktop target benefits from performance and rendering stability improvements, accessibility support on MacOS, and refined APIs for controlling user interactions.

Rendering fallbacks and improved software rendering

By default, Compose for Desktop uses hardware-accelerated rendering via DirectX, OpenGL, and Metal. However, there is a near-infinite combination of graphics hardware and drivers on the market that can introduce all kinds of rendering problems. Nevertheless, we want to ensure that any applications you build with Compose for Desktop still run on these systems.

To address this, we’re introducing automatic rendering fallbacks for Compose for Desktop. They ensure that even when certain rendering technologies have issues on the target system, your application will remain usable. For example, if the system running your application encounters issues with the DirectX renderer, Compose for Desktop will change to an OpenGL-based rendering strategy. If OpenGL has incompatibilities or problems, your app will automatically switch to software rendering.

The software renderer (the most basic and most widely compatible rendering backend in Compose for Desktop) also receives some massive performance boosts with this release. Our benchmarks show that the optimizations included with this release make the rendering process at least 50% faster, with some systems seeing rendering times being cut in half.

New mouse pointer APIs

In this release, the mouse pointer APIs have been reworked and extended. The pointerInput modifier has been extended with the PointerEventType.Enter and PointerEventType.Exit events, which can be retrieved inside an awaitPointerEventScope:

val text = remember{ mutableStateOf("Start")}
var modifier = Modifier.pointerInput(Unit) {
   while (true) {
       val event = awaitPointerEventScope { awaitPointerEvent() }
       when (event.type) {
           PointerEventType.Enter -> text.value = "Enter"
           PointerEventType.Exit -> text.value = "Left"
       }
   }
}

Button(onClick = {}, modifier) {
   Text(text.value)
}

This low-level API also serves as the foundation for the convenient new hoverable API.

Hoverables

We’re introducing an extra modifier for a common type of mouse events – hovering. This modifier allows you to access the hover state of a composable without having to use the lower-level mouse events API. You can instead query the hover state of a composable directly:

val interactionSource = remember { MutableInteractionSource() }
val isHovered by interactionSource.collectIsHoveredAsState()
Box(
   Modifier
       .hoverable(interactionSource = interactionSource)
       .background(if (isHovered) Color.Red else Color.Green)
       .size(128.dp)
)

Transparent window support

The era of purely rectangular Compose for Desktop windows is over! You can now make the background for your application’s window transparent. Together with disabled window decorations, this gives you full control over how the user interface is rendered. Whether your app uses custom rounded corners for its window or renders free-floating buttons on the user’s desktop, transparent windows provide access to a new category of designs that you can easily implement:

A sample user interface that uses custom window decorations and rounded corners by using Compose for Desktop’s transparent window support
fun main() = application {
   var isOpen by remember { mutableStateOf(true) }
   if (isOpen) {
       Window(
           onCloseRequest = { isOpen = false },
           title = "Transparent Window Example",
           transparent = true,
           undecorated = true, //transparent window must be undecorated
       ) {
           Surface(
               modifier = Modifier.fillMaxSize().padding(5.dp).shadow(3.dp, RoundedCornerShape(20.dp)),
               color = Color(55, 55, 55),
               shape = RoundedCornerShape(20.dp) //window has round corners now
           ) {
               Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.BottomEnd) {
                   Button(
                       onClick = { isOpen = false },
                       modifier = Modifier.padding(20.dp).align(Alignment.BottomCenter),
                       colors = ButtonDefaults.buttonColors(backgroundColor = Color(75, 75, 75)),
                   ) {
                       Text("Close", color = Color(200, 200, 200))
                   }
               }
           }
       }
   }
}

Preview: accessibility support on macOS

We want to make sure that the applications you build with Compose for Desktop are accessible to everyone, including users with disabilities. In order to increase the accessibility of your applications for blind users and those with low vision, this release includes preview support for Apple’s VoiceOver, the screen reader built into macOS.

So far, these accessibility features are only available on macOS, but we’re hoping to add support for Windows soon. Accessibility for cross-platform applications remains a technical challenge. We’ll talk more about making Compose Multiplatform accessible for everyone in a follow-up blog post on the topic.

What’s new in Compose for Web

The Beta version of Compose Multiplatform also comes with new additions to its web target. In Compose for Web, your composables create a DOM tree – a tree of HTML nodes, which the browser then renders using its layout engine. Now, we are adding support for composable scalable vector graphics.

Composable SVG support

With this release, we’ve extended the APIs to also allow you to declaratively define SVGs – Scalable Vector Graphics – using the @Composable API. This means you can now define and embed vector images in your web applications that react to changes in the application’s state, once again leveraging a powerful and type-safe Kotlin DSL.

@ExperimentalComposeWebSvgApi
@Composable
fun svgDemo() {
   Div() {
       Svg(viewBox = "0 0 200 200") {
           var currentColor by remember { mutableStateOf(0) }
           val colors = listOf(
               rgb(200, 0, 0),
               rgb(100, 0, 0),
               rgb(100, 20, 0),
               rgb(20, 100, 0)
           )
           SvgText("Click the circle!", x = 20, y = 20)
           Circle(100, 100, 20, {
               attr("fill", colors[currentColor].toString())
               onClick {
                   currentColor = (currentColor + 1).mod(colors.size)
               }
           })
       }
   }
}

We’re almost there: expect 1.0 soon!

Compose Multiplatform is now in Beta! Most of the APIs are now very close to being stable, and we are not expecting any major API changes before the final release. We’re putting the finishing touches on Compose Multiplatform, and you can look forward to its stable, 1.0 release sometime later this year.

Try out Compose Multiplatform Beta!

Whatever combination of web, desktop, and Android you may target – we hope you’ll give this version of Compose Multiplatform a try! Evaluate Compose Multiplatform for your production applications, or build your next MVP using our declarative UI framework!

We offer a variety of resources to help you get started:

If you’re upgrading an existing application, and want to use Compose Multiplatform Beta, add the following to your settings.gradle.kts file:

pluginManagement {
   repositories {
       gradlePluginPortal() //compose beta plugin is published here
   }
}

Additionally, make sure that you specify the correct dependencies for Compose Multiplatform in your build.gradle.kts:

import org.jetbrains.compose.*
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
   kotlin("jvm") version "1.5.31"
   id("org.jetbrains.compose") version "1.0.0-beta5"
   //add other plugins here if needed
}

repositories {
   google()
   mavenCentral()
   jetbrainsCompose()  //repository that contains Compose MPP artifacts
}

dependencies {
   implementation(compose.desktop.currentOs) //for desktop
   implementation(compose.web.core) //for web
   implementation(compose.web.svg) //for web
   implementation(compose.runtime) //for web
}

Share your feedback and join the discussion

The stable release of Compose Multiplatform is already on the horizon. That’s why we are once again asking for your feedback. Help us help you by reporting problems, telling us about APIs that you feel are missing, and requesting features you’d like to see. You can do all of this in the project’s issue tracker.

If you want to talk to other developers and team members, we also invite you to join the discussion on the Kotlin Slack. In the #compose-desktop and #compose-web channels, you can find discussions about Compose for Desktop and Web, respectively. In #compose you can discuss general topics involving Compose and Jetpack Compose for Android.

We can’t wait to see what you’ll build next with Compose Multiplatform! Take care!

See also