← 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. The layout and the interaction were identical over the 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 and one set of interaction states, so there's 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, so 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