Search code examples
androidandroid-jetpack-composevertical-alignmentcomposable

Vertical alignment of Row doesn't center properly


I'm programming a few examples from the Android training courses and i'm stuck on a little detail. I want to align a text composable and a switch composable vertically, but somehow the switch composable is settled always a little higher than the text composable. I made a simple app to demonstrate my problem. The app contains a column with a text, followed by a row (row contains a text and a switch) and finally a text again. Within the row, I want the text composable and the switch composable to be vertically aligned.

Code:

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.alignmenttest.ui.theme.AlignmentTestTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AlignmentTestTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    AlignmentTest()
                }
            }
        }
    }
}

@Composable
fun AlignmentTest(modifier: Modifier = Modifier) {
    Column {
        Text(
            "Text before",
            modifier = modifier.padding(bottom = 16.dp)
        )
        RowWithTextAndSwitch(modifier = Modifier.padding(bottom = 16.dp))
        Text("Text after")
    }
}

@Composable
fun RowWithTextAndSwitch(modifier: Modifier = Modifier) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text("Some text")
        Switch(
            checked = false,
            onCheckedChange = {},
            modifier = modifier
                .fillMaxWidth()
                .wrapContentWidth(Alignment.End)
        )
    }
}

@Preview(showBackground = true)
@Composable
fun AlignmentTestPreview() {
    AlignmentTestTheme {
        AlignmentTest()
    }
}

Preview with the Switch not being centered vertically:

And this is what I want:

enter image description here

I tried various align commands and checked the course instructions several times and couldn't find a solution. In the images of the course, the text composable and the switch composable are perfectly aligned vertically (I tried the code of the course and also other things).


Solution

  • This is a subtle problem with how you use your modifiers.

    The convention is that each composable component should have a modifier: Modifier = Modifier parameter. You already do that, so far so good.

    Now, this modifier should be applied to its first child composable, and only there. And here is the problem with your code: modifier of RowWithTextAndSwitch is not applied to Row, it is applied to Switch. Change it like this and it should work as expected:

    @Composable
    fun RowWithTextAndSwitch(modifier: Modifier = Modifier) {
        Row(
            modifier = modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Text("Some text")
            Switch(
                checked = false,
                onCheckedChange = {},
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentWidth(Alignment.End),
            )
        }
    }