How to align icon in button to the left and keep text centered
A

3

6

I am trying to align the icon of a button to the left and keep the text centered. Any ideas how this can be achieved?

My composable:

  @Composable
  fun CustomButton() {
    MaterialTheme {
      OutlinedButton(
        onClick = {},
        modifier = Modifier
          .padding(12.dp),
        colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
        shape = RoundedCornerShape(4.dp)) {
          Icon(
            imageVector = Icons.Default.FavoriteBorder,
            contentDescription = null,
            modifier = Modifier.padding(start = 4.dp)
          )
          Text(text = "Like", color = Color.Grey)
      }
    }
  }

This is how it looks now: enter image description here

Arabeila answered 13/3, 2021 at 16:2 Comment(0)
I
3

if you need a full width button, wrap the content with a Box then add fillMaxWidth() and TextAlign.Center to the text

@Composable
fun CustomButton() {
    MaterialTheme {
        OutlinedButton(
            onClick = {},
            modifier = Modifier.padding(12.dp),
            colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
            shape = RoundedCornerShape(4.dp)
        ) {
            Box {
                Text(
                    text = "Like",
                    color = Color.Gray,
                    modifier = Modifier.fillMaxWidth(),
                    textAlign = TextAlign.Center
                )
                Icon(
                    imageVector = Icons.Default.FavoriteBorder,
                    contentDescription = null,
                    modifier = Modifier.padding(start = 4.dp)
                )
            }
        }
    }
}

otherwise you can create a custom layout

@Composable
fun CustomButton() {
    MaterialTheme {
        OutlinedButton(
            onClick = {},
            modifier = Modifier.padding(12.dp),
            colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
            shape = RoundedCornerShape(4.dp)
        ) {
            Layout(
                content = {
                    Icon(Icons.Default.FavoriteBorder, null)
                    Text("Like", Modifier.padding(horizontal = 8.dp), Color.Gray)
                },
                measurePolicy = { measurables, constraints ->
                    val icon = measurables[0].measure(constraints)
                    val text = measurables[1].measure(constraints)
                    layout(
                        width = text.width + icon.width * 2,
                        height = maxOf(text.height, icon.height, constraints.minHeight)
                    ) {
                        icon.placeRelative(0, 0)
                        text.placeRelative(icon.width, 0)
                    }
                }
            )
        }
    }
}
Immortality answered 13/3, 2021 at 19:53 Comment(2)
This is great but in the scenario where the text is large, it overlaps with icon. Is there a way to ensure that text is centered within full button width, but still is constrained in such a way that it does not overlap with icon ?Mernamero
@TusharKathuria Check out my answer, icon and text will never overlap doing it that way.Jocasta
L
4

The content of the OutlinedButton is a RowScope.
You can apply the weight(1f) modifier to the Text and an offset(x= -iconWidth/2):

OutlinedButton(
    onClick = {},
    modifier = Modifier.fillMaxWidth(),
    shape = RoundedCornerShape(4.dp)
){
    Icon(
        imageVector = Icons.Default.FavoriteBorder,
        contentDescription = "contentDescription",
    )
    Text(
        text = "Like",
        textAlign = TextAlign.Center,
        modifier = Modifier
             .weight(1f)
             .offset(x= -12.dp) //default icon width = 24.dp
    )
}

enter image description here

Lola answered 21/9, 2022 at 22:12 Comment(0)
I
3

if you need a full width button, wrap the content with a Box then add fillMaxWidth() and TextAlign.Center to the text

@Composable
fun CustomButton() {
    MaterialTheme {
        OutlinedButton(
            onClick = {},
            modifier = Modifier.padding(12.dp),
            colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
            shape = RoundedCornerShape(4.dp)
        ) {
            Box {
                Text(
                    text = "Like",
                    color = Color.Gray,
                    modifier = Modifier.fillMaxWidth(),
                    textAlign = TextAlign.Center
                )
                Icon(
                    imageVector = Icons.Default.FavoriteBorder,
                    contentDescription = null,
                    modifier = Modifier.padding(start = 4.dp)
                )
            }
        }
    }
}

otherwise you can create a custom layout

@Composable
fun CustomButton() {
    MaterialTheme {
        OutlinedButton(
            onClick = {},
            modifier = Modifier.padding(12.dp),
            colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
            shape = RoundedCornerShape(4.dp)
        ) {
            Layout(
                content = {
                    Icon(Icons.Default.FavoriteBorder, null)
                    Text("Like", Modifier.padding(horizontal = 8.dp), Color.Gray)
                },
                measurePolicy = { measurables, constraints ->
                    val icon = measurables[0].measure(constraints)
                    val text = measurables[1].measure(constraints)
                    layout(
                        width = text.width + icon.width * 2,
                        height = maxOf(text.height, icon.height, constraints.minHeight)
                    ) {
                        icon.placeRelative(0, 0)
                        text.placeRelative(icon.width, 0)
                    }
                }
            )
        }
    }
}
Immortality answered 13/3, 2021 at 19:53 Comment(2)
This is great but in the scenario where the text is large, it overlaps with icon. Is there a way to ensure that text is centered within full button width, but still is constrained in such a way that it does not overlap with icon ?Mernamero
@TusharKathuria Check out my answer, icon and text will never overlap doing it that way.Jocasta
J
1

The following solution is similar to Code Poet's one, but I've removed the superfluous Boxes, there is no need for them:

@Composable
fun CustomButton() {
    // I left out the MaterialTheme from OP here as I don't think this should go here, but instead into the topmost container.
    Button(
        onClick = {},
        modifier = Modifier.padding(12.dp).fillMaxWidth(),
        colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
        shape = RoundedCornerShape(4.dp)) {
        Icon(
            imageVector = Icons.Default.FavoriteBorder,
            contentDescription = null,
            modifier = Modifier
                .padding(start = 4.dp)
                .height(32.dp) // or whatever size your icon should have
                .width(32.dp)
                .align(Alignment.CenterVertically)
        )
        Text(
            text = "Like", 
            color = Color.Grey,
            textAlign = TextAlign.Center,
            modifier = Modifier.weight(1f)
        )
        Spacer(modifier = Modifier.width((32+4).dp)) // width = 32dp icon width + 4dp padding
    }
}
Jocasta answered 21/6, 2022 at 21:33 Comment(1)
I like your answer the most, as the other answers that uses offset can get overlap issues when button width is small.Justin

© 2022 - 2024 — McMap. All rights reserved.