2

I have a Compose component with a box and 2 components inside it, but they are never visible both at the same time. I want to adjust size of this box to the first component and stay unchanged when this component will be not visible.

Box(
    modifier = modifier.then(Modifier.background(bgColor)),
    contentAlignment = Alignment.Center
) {
    if (componentVisible1) {
        Button(
            modifier = Modifier.height(48.dp).widthIn(144.dp),
            onClick = onClicked,
            enabled = enabled
        ) {
            Text(
                text = "text1",
            )
        }
    }

    if (component2Visible) {
        CircularProgressIndicator(
            modifier = Modifier.size(24.dp).background(buttonColor, CircleShape).padding(2.dp),
            strokeWidth = 2.dp
        )
    }
}

Now, width of the box reduces when the component1 is not visible.

AndroideuszPL
  • 385
  • 1
  • 2
  • 13

1 Answers1

4

You can use SubcomposeLayout to pass a Composable's dimension if another one depends on it without recomposition or in your case even if it's not even in composition.

@Composable
internal fun DimensionSubcomposeLayout(
    modifier: Modifier = Modifier,
    mainContent: @Composable () -> Unit,
    dependentContent: @Composable (DpSize) -> Unit
) {

    val density = LocalDensity.current
    SubcomposeLayout(
        modifier = modifier
    ) { constraints: Constraints ->

        // Subcompose(compose only a section) main content and get Placeable
        val mainPlaceable: Placeable = subcompose(SlotsEnum.Main, mainContent)
            .map {
                it.measure(constraints.copy(minWidth = 0, minHeight = 0))
            }.first()


        val dependentPlaceable: Placeable =
            subcompose(SlotsEnum.Dependent) {
                dependentContent(
                    DpSize(
                        density.run { mainPlaceable.width.toDp() },
                        density.run { mainPlaceable.height.toDp() }
                    )
                )
            }
                .map { measurable: Measurable ->
                    measurable.measure(constraints)
                }.first()


        layout(mainPlaceable.width, mainPlaceable.height) {
            dependentPlaceable.placeRelative(0, 0)
        }
    }
}

/**
 * Enum class for SubcomposeLayouts with main and dependent Composables
 */
enum class SlotsEnum { Main, Dependent }

And you can use it to set dimensions of Box based on measuring or subcomposing your Button.

@Composable
private fun DimensionSample() {

    var componentVisible1 by remember { mutableStateOf(false) }
    var component2Visible by remember { mutableStateOf(true) }
    Column {

        Button(onClick = {
            componentVisible1 = !componentVisible1
            component2Visible = !component2Visible
        }) {
            Text(text = "Toggle")
        }

        val button = @Composable {
            Button(
                modifier = Modifier
                    .height(48.dp)
                    .widthIn(144.dp),
                onClick = {}

            ) {
                Text(
                    text = "text1",
                )
            }
        }

        val circularProgressIndicator = @Composable {
            CircularProgressIndicator(
                modifier = Modifier
                    .size(24.dp)
                    .background(Color.Yellow, CircleShape)
                    .padding(2.dp),
                strokeWidth = 2.dp
            )
        }




        DimensionSubcomposeLayout(mainContent = {
            button()
        }) {
            Box(
                modifier = Modifier
                    .size(it)
                    .then(Modifier.background(Color.Red)),
                contentAlignment = Alignment.Center
            ) {
                if (componentVisible1) {
                    button()
                }

                if (component2Visible) {
                    circularProgressIndicator()
                }
            }
        }
    }
}

enter image description here

Thracian
  • 43,021
  • 16
  • 133
  • 222