← All shorts
49Compose Multiplatform 1.6+2 min read

Compose Multiplatform: Sharing UI, and When Not To

Compose Multiplatform shares full screens across Android and iOS via Skia, but native idioms are still sometimes the better call.

KMP shares business logic; Compose Multiplatform goes further and shares the UI itself, rendering the same @Composable tree through Skia on both Android and iOS instead of stopping at the ViewModel layer.

#The old way

A task checklist screen was written twice: LazyColumn and Checkbox on Android, List and Toggle on iOS — same layout, same interaction, same list of items, implemented in two declarative UI frameworks that don't share a line of code.

#The new way

data class Task(val id: String, val title: String, val done: Boolean)

@Composable
fun TaskListScreen(
    tasks: List<Task>,
    onToggle: (Task) -> Unit,
    modifier: Modifier = Modifier
) {
    LazyColumn(modifier = modifier.fillMaxSize()) {
        items(tasks, key = { it.id }) { task ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 16.dp, vertical = 8.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Checkbox(checked = task.done, onCheckedChange = { onToggle(task) })
                Text(text = task.title, modifier = Modifier.padding(start = 8.dp))
            }
        }
    }
}
fun MainViewController(tasks: List<Task>, onToggle: (Task) -> Unit) =
    ComposeUIViewController { TaskListScreen(tasks = tasks, onToggle = onToggle) }

TaskListScreen compiles for both androidMain and iosMain unchanged; ComposeUIViewController wraps it as a UIViewController that a SwiftUI UIViewControllerRepresentable or a plain UIKit push can host.

#Why it matters

  • One layout, one set of interaction states, one set of UI bugs to fix instead of two
  • Design changes (spacing, typography, a new state) ship to both platforms in a single PR
  • Still interoperates with native navigation — a Compose screen can sit inside an otherwise-native iOS nav stack

#Gotcha

Compose Multiplatform on iOS renders through Skia inside a hosted UIViewController, not through UIKit or SwiftUI's layout system — it doesn't automatically pick up SwiftUI's safe-area or keyboard-avoidance behavior. A TaskListScreen embedded next to native screens can render content under the notch or behind the keyboard unless you thread WindowInsets through it explicitly, something a pure SwiftUI screen gets for free. Screens dominated by platform-native transitions, widgets, or heavy accessibility customization are often still better left native.

kmpkotlincross-platform

Related shorts