Compose Multiplatform: Building Cross-Platform UIs with Jetpack Compose

Jetpack Compose has revolutionized Android UI development, providing a modern, declarative approach. Its architecture allows developers to leverage Kotlin Multiplatform, expanding UI capabilities beyond Android. This blog post explores using Compose Multiplatform libraries in Jetpack Compose, focusing on benefits, implementation, and practical examples.

What is Compose Multiplatform?

Compose Multiplatform, built by JetBrains, is a declarative UI framework that enables code sharing across multiple platforms, including Android, iOS, desktop, and web. It leverages Kotlin’s multiplatform capabilities, allowing developers to write UI code once and deploy it on different platforms.

Why Use Compose Multiplatform Libraries in Jetpack Compose?

  • Code Reuse: Write UI components once and use them across different platforms, reducing development time and effort.
  • Consistent UI: Maintain a consistent look and feel across all your applications.
  • Kotlin Power: Benefit from Kotlin’s modern features, safety, and performance.
  • Ecosystem Integration: Integrate with platform-specific libraries while sharing common UI components.

Setting Up a Compose Multiplatform Project

Before diving into specific libraries, let’s set up a basic Compose Multiplatform project. This example will target Android and Desktop.

Step 1: Create a New Project

Use the Kotlin Multiplatform wizard in IntelliJ IDEA to create a new project. Select “Compose Multiplatform” as the template and specify the target platforms (Android and Desktop).

Step 2: Project Structure

The project structure will typically include:

  • commonMain: Common code shared between all platforms.
  • androidMain: Android-specific code.
  • desktopMain: Desktop-specific code.

Step 3: Dependencies

Add the necessary dependencies in your build.gradle.kts file (for the commonMain source set):


kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material)
                implementation(compose.ui)
                @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
                implementation(compose.components.resources)
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("androidx.appcompat:appcompat:1.6.1")
                implementation("androidx.core:core-ktx:1.12.0")
                implementation(compose.uiToolingPreview)
                debugImplementation(compose.uiTooling)
            }
        }
        val desktopMain by getting {
            dependencies {
                implementation(compose.desktop.common)
                implementation(compose.desktop.currentOs)
            }
        }
    }
}

Compose Multiplatform Libraries: Examples

Let’s explore some practical examples of using Compose Multiplatform libraries in your projects.

Example 1: Sharing UI Components

Create a simple UI component in the commonMain source set that can be used on both Android and Desktop.


// commonMain/kotlin/MyComponent.kt
import androidx.compose.material.Text
import androidx.compose.runtime.Composable

@Composable
fun MySharedComponent(text: String) {
    Text("Shared Component: $text")
}

Use this component in both Android and Desktop applications.


// androidMain/kotlin/MainActivity.kt
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.example.myapplication.MySharedComponent

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MySharedComponent("Hello from Android!")
        }
    }
}

// desktopMain/kotlin/Main.kt
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.example.myapplication.MySharedComponent

fun main() = application {
    Window(onCloseRequest = ::exitApplication, title = "Desktop App") {
        MySharedComponent("Hello from Desktop!")
    }
}

@Preview
@Composable
fun AppPreview() {
    MySharedComponent("Hello from Desktop Preview!")
}

Example 2: Using Resources

Compose Multiplatform enables sharing resources such as images and strings across platforms. Use the org.jetbrains.compose.components.resources library.

Step 1: Add Resources

Place your resources (e.g., images) in the commonMain/resources directory.

Step 2: Access Resources

// commonMain/kotlin/ResourceComponent.kt
import androidx.compose.runtime.Composable
import org.jetbrains.compose.components.resources.painterResource
import androidx.compose.foundation.Image
import androidx.compose.ui.res.painterResource

@Composable
fun MyResourceComponent() {
    Image(
        painter = painterResource("compose-multiplatform.xml"), // Replace with your image name
        contentDescription = "Shared Image"
    )
}

Use this component in your Android and Desktop applications as before.

Example 3: Platform-Specific Implementations

Sometimes, you need platform-specific logic while sharing UI. Use expect and actual declarations for this.


// commonMain/kotlin/PlatformUtils.kt
expect fun getPlatformName(): String

// androidMain/kotlin/PlatformUtils.kt
actual fun getPlatformName(): String = "Android"

// desktopMain/kotlin/PlatformUtils.kt
actual fun getPlatformName(): String = "Desktop"

// commonMain/kotlin/PlatformComponent.kt
import androidx.compose.material.Text
import androidx.compose.runtime.Composable

@Composable
fun PlatformComponent() {
    Text("Running on: ${getPlatformName()}")
}

This allows you to display different platform names while sharing the UI component.

Best Practices for Compose Multiplatform

  • Separate Concerns: Keep UI logic in commonMain and platform-specific code in respective source sets.
  • Resource Management: Utilize shared resources for images, strings, and other assets to maintain consistency.
  • Testing: Implement thorough testing strategies for each platform to ensure compatibility and functionality.
  • Dependency Management: Carefully manage dependencies to avoid conflicts and ensure compatibility across platforms.

Conclusion

Compose Multiplatform libraries offer a powerful way to build cross-platform applications with Jetpack Compose. By sharing UI components, resources, and platform-specific implementations, developers can significantly reduce development time and maintain a consistent user experience across multiple platforms. Embracing Compose Multiplatform enhances the versatility and efficiency of your projects, making it an excellent choice for modern application development.