Search code examples
androidkotlinandroid-jetpack-compose

How can I maintain the size when offset modifier is applied to a Box?


My intention is to create an animation in which the black box moves horizontally. To do this, if I press the second button I increase the offset (x coordinate) and if I press the first button I return to the initial state.

@Composable
fun Screen() {
    val offsetX = remember { Animatable(0f) }
    val coroutineScope = rememberCoroutineScope()

    Column(
        Modifier
            .fillMaxSize()
            .padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Box(
            modifier = Modifier
                .height(40.dp)
                .fillMaxWidth()
                .clip(CircleShape)
                .background(Color.White)
                .padding(horizontal = 4.dp, vertical = 2.dp),
        ) {
            Box(
                modifier = Modifier
                    .size(100.dp, 40.dp)
                    .clip(CircleShape)
                    .offset(x = offsetX.value.dp)
                    .background(Color.Black, CircleShape),
            )
        }

        Button(onClick = {
            coroutineScope.launch {
                offsetX.animateTo(0f, tween(400))
            }
        }) {
            Text("reset")
        }

        Button(onClick = {
            coroutineScope.launch {
                offsetX.animateTo(80f, tween(400))
            }
        }) {
            Text("80")
        }
    }
}

I have tried to create the animation of moving the box by modifying the offset (x coordinate). However, when I increase the offset, the start side moves but not the end side, so the effect it has is to decrease the width, instead of moving, which is what I want to do.

Initial black box position compared to after modifying the offset:


Solution

  • The offset must come before the size modifier:

    modifier = Modifier
        .offset(x = offsetX.value.dp)
        .size(100.dp, 40.dp)
        .clip(CircleShape)
        .background(Color.Black, CircleShape)
    

    The size modifier determines where the content ends. When you apply offset afterwards you only change where the content begins, the end will stay where it is and there is no additional size measurement taking place. Although not explicit about the consequences, this is also explained in the documentation:

    Applying an offset only changes the position of the content, without interfering with its size measurement.