Kotlin Multiplatform Help

Explore composable code

Let's examine closely the sample composable created by the Kotlin Multiplatform wizard. First, there is the composable App() function that implements the common UI and can be used on all platforms. Second, there is the platform-specific code that launches this UI on each platform.

Implementing composable functions

In the shared/src/commonMain/kotlin/App.kt file, take a look at the App() function:

@Composable @Preview fun App() { MaterialTheme { var showContent by remember { mutableStateOf(false) } Column( modifier = Modifier .background(MaterialTheme.colorScheme.primaryContainer) .safeContentPadding() .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, ) { Button(onClick = { showContent = !showContent }) { Text("Click me!") } AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, ) { Image(painterResource(Res.drawable.compose_multiplatform), null) Text("Compose: $greeting") } } } } }

The App() function is a regular Kotlin function annotated with @Composable. Such functions are referred to as composable functions or simply composables. They are the building blocks of a UI based on Jetpack Compose or Compose Multiplatform.

This App() function is used as the base of the UI architecture for the app and has the following structure:

  • The MaterialTheme() sets the look of the application. The default settings can be customized. For example, you can choose colors, shapes, and typography.

  • The Column() composable controls the layout of the application. Here, it displays a Button above the AnimatedVisibility() composable.

  • The Button() contains the Text composable, which renders some text on top of the button.

  • The AnimatedVisibility() call is set up to show and hide the Image using an animation when the button is pressed.

  • The painterResource() loads a vector image stored as an XML file.

The horizontalAlignment parameter of the Column() function centers the column's content. For this to come into effect, the column should take up the full width of its container. You can achieve this by using the modifier parameter.

Modifiers are a key component of Jetpack Compose and Compose Multiplatform. They provide the primary mechanism to adjust the appearance or behavior of composables in the UI. Modifiers are created using methods of the Modifier type. When you chain these methods, each call can change the Modifier returned from the previous call, making the order significant. See the Compose Multiplatform introduction to modifiers and the extensive Jetpack Compose modifier documentation for more details.

Managing the state

The loaded image has a persistent quality: it should consistently remain either visible or hidden across recompositions unless user clicks the button. The showContent property in the App() composable is built using the mutableStateOf() function, which means it's a state object that can be observed:

var showContent by remember { mutableStateOf(false) }

The state object is wrapped in a remember() call, meaning that it's built once and then retained by the framework. This way, the showContent property has a value that is a state object containing a boolean. The framework caches this state object, allowing composables to observe it.

When the value of the state changes, any composables that observe it are re-invoked. This allows any of the widgets they produce to be redrawn. This is called a recomposition.

The only place where the state is changed is in the onClick parameter of the Button() call. The event handler flips the value of the showContent property. As a result, the image gets shown or hidden along with a Greeting().greet() call because the parent AnimatedVisibility() composable observes showContent.

Launching UI on different platforms

The App() function is executed differently on each platform:

  • On Android, it's managed by an activity.

  • On iOS, by a view controller.

  • On the desktop, by a window.

  • On the web, by a container.

Let's examine each of them.

On Android

For Android, open the MainActivity.kt file within androidApp/src/main/kotlin:

class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { App() } } }

This is an Android activity called MainActivity that invokes the App() composable declared in common code.

On iOS

For iOS, open the MainViewController.kt file within shared/src/iosMain/kotlin:

fun MainViewController() = ComposeUIViewController { App() }

This is a view controller that performs the same role as an activity on Android. Notice that both the iOS and Android types simply invoke the App() composable from common code.

On desktop

For desktop, look for the main.kt file in desktopApp/src/main/kotlin:

fun main() = application { Window( onCloseRequest = ::exitApplication, title = "ComposeDemo" ) { App() } }
  • Here, the application() function launches a new desktop application. This function takes a lambda, which initializes the UI.

  • Typically, within the application() function, you create a Window and specify its properties as well as instructions for the program that should be executed when the window is closed (onCloseRequest). In the default project, the whole application shuts down (::exitApplication).

  • Inside the window, you can place your content. As with Android and iOS, the only content is the UI layout provided by the App() composable.

In this example, the App() function doesn't take any parameters. In a larger application, you typically pass parameters to platform-specific dependencies. These dependencies could be written manually or passed using a dependency injection library.

On web

In the main.kt file within the webApp/src/webMain/kotlin/ directory, take a look at the main() function:

@OptIn(ExperimentalComposeUiApi::class) fun main() { ComposeViewport { App() } }
  • The @OptIn(ExperimentalComposeUiApi::class) annotation tells the compiler that you are using a Compose API marked as experimental and may change in future releases.

  • The ComposeViewport{} function sets up the Compose environment for the application.

  • The web app is inserted into the container specified as a parameter for the ComposeViewport function.

  • The App() function is responsible for building the UI components of your application using Jetpack Compose.

Next step

In the next part of the tutorial, you'll add a dependency to the project and modify the user interface.

Proceed to the next part

Get help

15 May 2026