Ladoken
Ladoken

Reputation: 113

Does a 0dp sized composable even get composed

Does a composable of size 0dp even get composed or recomposed or is it just ignored by jetpack compose at runtime? This is to know if performance wise, hiding a composable using an if statement is the same as setting its size to 0dp.

Upvotes: 4

Views: 737

Answers (1)

Thracian
Thracian

Reputation: 67383

Short answer, yes. Composables enter and exit composition based on on UI structure not size modifier, Under the hood of Jetpack Compose — part 2 of 2 article explains this very well. Conditional Composable blocks enter composition when the conditions are met and stay in composition, or recomposed if the state they read changes then exit composition when condition is no longer valid. You can check this answer for conditional composition.

For example you can set in example below that Composable is laid out and drawn even if it has 0.dp size

@Composable
private fun CompositionSample() {

    val context = LocalContext.current

    Box(
        Modifier
            .size(0.dp)
            .layout { measurable, constraints ->
                val placeable = measurable.measure(constraints)

                Toast
                    .makeText(
                        context,
                        "Layout Phase width: ${placeable.width}, height: ${placeable.height}",
                        Toast.LENGTH_SHORT
                    )
                    .show()
                layout(placeable.width, placeable.height) {
                    placeable.placeRelative(0, 0)
                }
            }
            .drawWithContent {
                Toast
                    .makeText(context, "Draw Phase $size", Toast.LENGTH_SHORT)
                    .show()
                drawContent()
            }
    ) {
        Toast.makeText(context, "BoxScope", Toast.LENGTH_SHORT).show()
        Column {
            Toast.makeText(context, "ColumnScope", Toast.LENGTH_SHORT).show()
        }
    }
}

Even if a Composable is not visible on screen yet because of verticalScroll() or horizontalScroll() modifiers it enters composition. That's why LazyLists which subcomposes items on screen, and another one on scroll direction that is not visible yet, is more performant compapared to scroll modifiers.

@Composable
private fun HorizontalComposableSample() {

    val context = LocalContext.current

    Row(
        Modifier
            .fillMaxWidth()
            .horizontalScroll(rememberScrollState())
    ){
        Box(
            Modifier
                .size(1000.dp, 200.dp)
                .background(Color.Red))
        Column(modifier= Modifier
            .size(200.dp)
            .background(Color.Green)) {
            Toast.makeText(context, "ColumnScope", Toast.LENGTH_SHORT).show()
        }
    }
}

Composables don't have to be a UI elements to enter or exit composition either.

@Composable
private fun NonUIComposableSample() {

    val context = LocalContext.current

    var counter by remember { mutableStateOf(0) }
    var color by remember { mutableStateOf(Color.Red) }

    if (counter in 3..5) {
        DisposableEffect(Unit) {

            Toast.makeText(context, "Entering Composition counter: $counter", Toast.LENGTH_SHORT).show()
            color = Color.Yellow
            onDispose {
                color = Color.Green
                Toast.makeText(context, "Exiting Composition counter: $counter", Toast.LENGTH_SHORT).show()
            }
        }
    }

    Button(onClick = { counter++ }) {
        Text("Counter: $counter", color = color)
    }

}

In this example Block with DisposableEffect enters composition when counter is 3, stays in composition while counter is less than 6, then exits when condition is not met anymore.

Upvotes: 4

Related Questions