Search code examples
kotlinmaterial-uiandroid-jetpack-compose

How to center OutlinedTextField and FilledIconButton vertically in Row?


I have an OutlinedTextField with a FilledIconButton next to it in a Row. I set the verticalAlignment to Alignment.CenterVertically on the Row, but the button is slightly offset to the top. When I look at the Preview, the OutlinedTextField seems to be in one container that fits its size, but the FilledIconButton seems to be centered in a bigger container that also contains the other container but has a bit of padding at the top, causing the button to be offset to the top.

@Composable
fun PlayerInputField(
    playerName: String,
    onNameChange: (String) -> Unit,
    onAddPlayer: () -> Unit,
    onDeletePlayer: () -> Unit,
    commited: Boolean = false,
) {
    Row(
        modifier = Modifier
            .padding(horizontal = 10.dp, vertical = 0.dp)
            .wrapContentSize(),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        OutlinedTextField(
            value = playerName,
            onValueChange = onNameChange,
            modifier = Modifier.weight(1f),
            label = { Text("Player Name") },
            singleLine = true
        )
        if (!commited) {
            FilledIconButton(onClick = onAddPlayer) {
                Icon(Icons.Filled.Add, "Add player")
            }
        } else {
            FilledIconButton(onClick = onDeletePlayer) {
                Icon(Icons.Filled.Clear, "Remove player")
            }
        }
    }
}

Preview View Preview View

I already tried to set the padding on the Row to 0 and moving the verticalAlignment from the Row to a Modifier on the button and text field, but nothing changed.


Solution

  • That empty space on top of OutlinedTextField is necessary to display label composable when value is not empty. If you remove the label argument, text field will align vertically with the button.

    If you check OutlinedTextField source code, you'll see that it adds a 8.dp top padding when label is present. That padding is also specified in m3 specs. So, to keep the button visually aligned with the text field, add the same padding to it.

    //...
    FilledIconButton(
        modifier = Modifier.padding(top = 8.dp),
        onClick = onAddPlayer,
    ) {
    //...