Home > Software design >  Composable fillMaxSize and rotation not working
Composable fillMaxSize and rotation not working

Time:07-04

How can I rotate composables and still make them fill their parent?

For example, I have a Column filling the Screen with two Boxes taking up half the size. The size of the boxes seems to be calculated before the rotation and not after.

Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(8.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            Box(
                modifier = Modifier
                    .rotate(90f)
                    .fillMaxSize()
                    .weight(1f)
                    .background(Color.Red),
                contentAlignment = Alignment.Center
            ) {
                Text("1", fontSize = 100.sp)
            }
            Box(
                modifier = Modifier
                    .rotate(90f)
                    .fillMaxSize()
                    .weight(1f)
                    .background(Color.Blue),
                contentAlignment = Alignment.Center
            ) {
                Text("2", fontSize = 100.sp)
            }
}

enter image description here

Edit after Thracians comment:

This looks better but the maxHeight I get seems wrong, I can see in the layout inspector that the size of the BoxWithConstraints is right

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(8.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        BoxWithConstraints(modifier = Modifier.fillMaxSize().weight(1f)) {
            Box(
                modifier = Modifier
                    .rotate(90f)
                    .width(maxHeight)
                    .height(maxWidth)
                    .background(Color.Red),
                contentAlignment = Alignment.Center
            ) {
                Text("1", fontSize = 100.sp)
            }
        }
        BoxWithConstraints(modifier = Modifier.fillMaxSize().weight(1f)) {
            Box(
                modifier = Modifier
                    .rotate(90f)
                    .width(maxHeight)
                    .height(maxWidth)
                    .background(Color.Blue),
                contentAlignment = Alignment.Center
            ) {
                Text("2", fontSize = 100.sp)
            }
        }

enter image description here

CodePudding user response:

Modifier.rotate is

@Stable
fun Modifier.rotate(degrees: Float) =
    if (degrees != 0f) graphicsLayer(rotationZ = degrees) else this

Modifier.graphicsLayer{} does not change dimensions and physical position of a Composable, and doesn't trigger recomposition which is very good for animating or changing visual presentation.

You can also see in my question enter image description here

@Composable
private fun MyComposable() {
    Column(modifier = Modifier.fillMaxSize()) {

        Spacer(modifier = Modifier.height(300.dp))


            Column(modifier = Modifier.fillMaxSize()) {

                var angle by remember { mutableStateOf(0f) }

                LaunchedEffect(key1 = Unit) {
                    delay(2000)
                    angle = 90f
                }


                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .weight(1f)
                        .background(Color.Red)
                        .rotate(angle),
                    contentAlignment = Alignment.Center
                ) {
                    Text("1", fontSize = 100.sp)
                }

                Spacer(modifier =Modifier.height(8.dp))

                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .weight(1f)
                        .background(Color.Blue)
                        .rotate(angle),
                    contentAlignment = Alignment.Center
                ) {
                    Text("2", fontSize = 100.sp)
                }
            }
        }
}

enter image description here

Added Spacer to have uneven width and height for Boxes for demonstration.

As i posted in example gif order of rotate determines what we rotate.

modifier = Modifier .fillMaxSize() .weight(1f) .background(Color.Blue) .rotate(angle)

This sets the size, it never rotates parent but the content or child because we set size and background before rotation. This answer works if width of the child is not greater than height of the parent.

If child has Modifier.fillSize() or child's width is bigger than parent's height when we rotate as in the image left below. So we need to scale it back to parent after rotation since we didn't change parents dimensions.

@Composable
private fun MyComposable2() {
    var angle by remember { mutableStateOf(0f) }

    LaunchedEffect(key1 = Unit) {
        delay(2000)
        angle = 90f
    }

    

    Column(modifier = Modifier.fillMaxSize()) {

        Spacer(modifier = Modifier.height(100.dp))

        BoxWithConstraints(
            modifier = Modifier
                .fillMaxSize()
                .padding(8.dp)
        ) {

            val width = maxWidth
            val height = maxHeight / 2

            val newWidth = if (angle == 0f) width else height
            val newHeight = if (angle == 0f) height else width


            Column(modifier = Modifier.fillMaxSize()) {

                Box(
                    modifier = Modifier
                        .border(3.dp, Color.Red)
                        .size(width, height)
                        .graphicsLayer {
                            rotationZ = angle
                            scaleX = newWidth/width
                            scaleY = newHeight/height
                        }
                        .border(5.dp, Color.Yellow),
                    contentAlignment = Alignment.Center
                ) {

                    Image(
                        modifier = Modifier
                            .border(4.dp, getRandomColor())
                            .fillMaxSize(),
                        painter = painterResource(id = R.drawable.landscape1),
                        contentDescription = "",
                        contentScale = ContentScale.FillBounds
                    )
                }

                Box(
                    modifier = Modifier
                        .border(3.dp, Color.Red)
                        .graphicsLayer {
                            rotationZ = angle
                            scaleX = newWidth/width
                            scaleY = newHeight/height
                        }
                        .size(width, height)
                        .border(5.dp, Color.Yellow),
                    contentAlignment = Alignment.Center
                ) {

                    Image(
                        modifier = Modifier
                            .border(4.dp, getRandomColor())
                            .fillMaxSize(),
                        painter = painterResource(id = R.drawable.landscape1),
                        contentDescription = "",
                        contentScale = ContentScale.FillBounds
                    )
                }

            }
        }
    }
}

on left we don't scale only rotate as in question on right we scale child into parent based on height/width ratio

enter image description here enter image description here

  • Related