Skip to content

1. Getting Started

pablichjenkov edited this page Feb 28, 2023 · 10 revisions

The best way to get started is by creating a HelloWorld type of app, are you ready?

A simple Component will look something like this:

class HelloWorldComponent : Component() {

    private val helloWorldState = HelloWorldState()

    @Composable
    override fun Content(modifier: Modifier) {
        // We want to handle back press events in this Component so we add a BackPressHandler
        BackPressHandler { backPressedCallbackHandler.onBackPressed() }
        HelloWorldView(helloWorldState)
    }

}

@Composable
private fun HelloWorldView(helloWorldState: HelloWorldState) {

    val userName = helloWorldState.userName

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Enter your name:")
        OutlinedTextField(
            value = userName,
            onValueChange = {
                helloWorldState.userName = it
            }
        )
        Text("Great to meet you: $userName")
    }
}

private class HelloWorldState {
    var userName by mutableStateOf("")
}

When you run above code you will get the following screen outputs:

uistate3-hello-world-android uistate3-hello-world-jvm uistate3-hello-world-web

Woola!
You have created an application that runs in all the platforms. The code in each platform is pretty much the same and looks like bellow:

// Android
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CompositionLocalProvider(
                LocalBackPressedDispatcher provides AndroidBackPressDispatcher(this@MainActivity),
            ) {
                ComposeApp(
                    onExit = { finish() }
                )
            }
        }
    }
}

// JVM
fun main() =
    singleWindowApplication(
        title = "Hello World",
        state = WindowState(size = DpSize(500.dp, 800.dp))
    ) {
        val jvmBackPressDispatcher = remember {
            DefaultBackPressDispatcher()
        }

        CompositionLocalProvider(
            LocalBackPressedDispatcher provides jvmBackPressDispatcher,
        ) {
            Box(modifier = Modifier.fillMaxSize()) {
                ComposeApp(
                    onExit = {
                        exitProcess(0)
                    }
                )
                FloatingButton(
                    modifier = Modifier.offset(y = 48.dp),
                    alignment = Alignment.TopStart,
                    onClick = { jvmBackPressDispatcher.dispatchBackPressed() }
                )
            }
        }
    }

// Browser
fun main() {
    onWasmReady {
        Window("Hello World") {
            val webBackPressDispatcher = remember {
                DefaultBackPressDispatcher()
            }

            CompositionLocalProvider(
                LocalBackPressedDispatcher provides webBackPressDispatcher,
            ) {
                Box(modifier = Modifier.fillMaxSize()) {
                    ComposeApp(
                        onExit = {
                            // Should hide back button
                        }
                    )
                    FloatingButton(
                        modifier = Modifier.offset(y = 48.dp),
                        alignment = Alignment.TopStart,
                        onClick = { webBackPressDispatcher.dispatchBackPressed() }
                    )
                }
            }
        }
    }
}

In the code above, in the JVM and the Browser platforms, I added a FloatingButton{} composable to artificially mock a back button type of navigation. This platforms do not have a hardware back button built in and I wanted one, but is up to you, a back button is not needed in these platforms anyways.

As you can see, the code is pretty much the same in all the platforms and the good thing is, it barely change per app. The only code that will vary per app, is the ComposeAppState class.

class ComposeAppState {

    private val HelloWorldComponent: Component = HelloWorldComponent()
    private var onBackPressEventLastCopy: () -> Unit = {}

    init {
        HelloWorldComponent.context. rootBackPressedCallbackDelegate = ForwardBackPressCallback {
            onBackPressEventLastCopy()
        }
    }

    fun start() {
        HelloWorldComponent.start()
    }

    fun stop() {}

    @Composable
    fun PresentContent(
        onBackPressEvent: () -> Unit = {}
    ) {
        onBackPressEventLastUpdate = onBackPressEvent
        HelloWorldComponent.Content(Modifier)
    }

}

Code taken from HelloWorld example.

In this class body you will decide what component type to create as a root of all components and how to build your tree and interact with the different platforms apis. Now that you know how to create a component-tree App you will go to a more complex case, container components.

Parent Components

Clone this wiki locally