I have a a text element with an icon to its right, both wrapped in a Row. The text has a 1.0f weight with fill = false. When a word wraps to the next line, the text has some padding at the end of it causing the icon to be too far apart from it. This is what it looks like:
App Example
Here is the code:
Row(
modifier = Modifier.width(150.dp)
) {
Text(
"John Doe John Doe John Doe",
modifier = Modifier.weight(1.0f, fill = false).background(Color.Green)
)
Icon(
imageVector = Icons.Default.Done,
contentDescription = "",
)
}
How do I make it so the text's width wraps itself without adding that extra spacing?
This is a normal behavior. It happens because you set weight for your Text and a width modifier for your Row.
It's kind of unavoidable when setting a specific width or height. It may happen even if you set weight for both Composables inside the Row (0.1f and 0.9f). Because the word can't fit in the remaining space but the Composable must fill the Row
You set the row width to 150.dp and when your Text has a weight modifier, it fills the row even if a word in your Text composable doesn't fit in the line.
I have a Row layout with two Composables inside:
Text that should wrap itself width and take as much space as possible when content is lengthy
Image that has to be present every time on the right side of Text
To achieve both, I'm currently using weight(weight = 1f, fill = false) for Text Composable. And it works fine for single line content. However, it creates an issue with line breaking and wrapping multiline text. For clearer view, I've added background(Color.Yellow) and some long words. See reference code:
Row {
Text(
text = "This is the message with quite super-unprecedented words",
modifier = Modifier
.background(Color.Yellow)
.weight(
weight = 1f,
fill = false
)
)
Image(
painter = painterResource(R.drawable.image),
contentDescription = null
)
}
And the result looks like this:
Is it possible to remove the padding at the end of Text so Image could fit closely to Text in multiline case as well? Or any other ideas how to achieve this? I've already taken a look on softWrap parameter for Text and requireSize for Image but with no big luck.
I want to position my own component (say a Text component) vertically so that I can specify the y-offset relative to the bottom of the Text component. How could I do this in Jetpack compose?
So something like
Column {
Text("Something", modifier = Modifier.offset(y=10.dp))
}
But instead of 10dp representing the top y-position of the Text component it would be the bottom y-position. Basically taking into account the height of the Text even if Text size changes. So y = offset.y - height
As I can see it, there's two problems:
The font size can be changed, so I cannot hard code the text height.
I need to know the size of my Text component during composition, but I don't know how to get that.
You could go for custom Composables,
#Composable
fun CustomText(y: Dp){
Layout(content = { Text(text = "Lorem Ipsum") }){measurables, constraints ->
val text = measurables[0].measure(constraints)
layout(constraints.maxWidth, constraints.maxHeight){ //Change these per your needs
text.placeRelative(IntOffset(0, y.value.roundToInt() - text.height))
}
}
}
You could also use a custom Modifier. Check out using the layout modifier
I have a Row with max width, I want to place a Icon at start then a text and then another Icon at end.
I have specific sizes of icon and I want the Text to fill up the remaining space.
Row(
Modifier
.fillMaxWidth()
.height(30.dp)
){
Icon(Modifier.size(20.dp))
Text() // Fill this with remaining space available
Icon(Modifier.size(20.dp))
}
If I do fillMaxWidth in text then Icons goes out of the view.
How to do that?
You can apply Modifier.weight(1f) to the Text composable.
Something like:
Row(
Modifier
.fillMaxWidth()
.height(30.dp)
){
Icon(Icons.Filled.Add,"", Modifier.size(20.dp))
Text("Text",Modifier.weight(1f)) // Fill this with remaining space available
Icon(Icons.Filled.Add,"", Modifier.size(20.dp))
}
How exactly can you add Margin in Jetpack Compose?
I can see that there is a Modifier for padding with Modifier.padding(...) but I can't seem to find one for margins or am I blind?
Someone guide me please.
Thank you very much.
You can consider padding and margin as the same thing (imagine it as "spacing"). A padding can be applied twice (or more) in the same composable and achieve the similar behavior you would get with margin+padding. For example:
val shape = CircleShape
Text(
text = "Text 1",
style = TextStyle(
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center),
modifier = Modifier.fillMaxWidth()
.padding(16.dp)
.border(2.dp, MaterialTheme.colors.secondary, shape)
.background(MaterialTheme.colors.primary, shape)
.padding(16.dp)
)
Will result on this:
As you can see, the first padding is adding a space between the component and its border. Then the background and border are defined. Finally, a new padding is set to add space between the border and the text.
Thinking in terms of padding and margin you refer to the so-called box model that we are used to. There's no a box model in Compose but a sequence of modifiers which is applied to a given composable. The trick is that you can apply the same modifier like padding or border multiple times and the order of these matters, for example:
#Composable
fun PaddingExample() {
Text(
text = "Hello World!",
color = Color.White,
modifier = Modifier
.padding(8.dp) // margin
.border(2.dp, Color.White) // outer border
.padding(8.dp) // space between the borders
.border(2.dp, Color.Green) // inner border
.padding(8.dp) // padding
)
}
As the result you'll get this composable:
This design is well explained in the Modifiers documentation:
Note: The explicit order helps you to reason about how different modifiers will interact. Compare this to the view-based system where you had to learn the box model, that margins applied "outside" the element but padding "inside" it, and a background element would be sized accordingly. The modifier design makes this kind of behavior explicit and predictable, and gives you more control to achieve the exact behavior you want.
You can also use Spacer:
Spacer(modifier = Modifier.width(10.dp))
It represents an empty space layout, whose size can be defined using Modifier.width, Modifier.height and Modifier.size modifiers.
Suppose you want to add margin between 2 composables, then you can achieve it as
Text(
text = stringResource(id = R.string.share_your_posters),
fontSize = 16.sp,
color = Color.Black
)
Spacer(modifier = Modifier.width(10.dp))
Image(painter = painterResource(id = R.drawable.ic_starts), contentDescription = null)
The margin is different than padding, margin is the space outside the widget, where padding is the distance inside the widget, in old XML you could have decided explicitly which one to use, however the new compose way is different.
How compose treat paddings and margins?
There is an object which can be set as Parameter to the composable called Modifier, you can use this to do both margins and paddings.
Example of Padding:
Text(
text = "Test",
modifier = Modifier
.padding(16.dp)
.clickable { }
)
Example of Margin
Text(
text = "Test",
modifier = Modifier
.clickable { }
.padding(16.dp)
)
As you can see the order makes a difference here in compose, all the modifiers are implemented by order.
So from what I can understand after reading the documentation there is no margin modifier as such as the API designer felt it is redundant to give something different name which essentially does the same thing.
So let's say you want to apply a margin of 8dp before colouring your container with yellow background and you want the container with a padding of 4dp for the content.
Column(modifier = Modifier.padding(all = 8.dp)
.background(color = Color.Yellow)
.padding(all=4.dp)) {
Text(text = "Android")
...
}
Here in the above example you can see that I have applied the padding first and after that I have added background colour to the container and finally the last padding. And here's how it looks. Just like we intended.
I was also looking for something which should give me a direct option to set margin on a View like TextView. But unfortunately we don't have margin support in Jetpack compose. But the good news is we can still achieve it by using layout container like Box, which allows us to add views like TextView, ImageView etc.
So you can add margin to any of the child(TextView) by using padding modifier to the parent(Box).
Here is the code:
Box(Modifier.padding(10.dp)) {
Surface(color = Color.LightGray) {
Text(text = "Hello $text!", color = Color.Blue,
modifier = Modifier.padding(16.dp))
}
}
And the result is:
Here I have given 10.dp padding to the box.
Hope it is useful.
You can achieve the same effect as margin with putting your content, that has padding, inside a different composable like Box and make outer composable clickable. With this approach, inner padded areas will be included in clickable content.
You can achieve a margin effect by using nested Surface elements with padding
e.g.
#Composable
fun MainScreen() {
Surface(color=Color.Yellow, modifier=Modifier.padding(10.dp)){
Surface(color=Color.Magenta, modifier=Modifier.padding(30.dp)) {
Surface(
color = Color.Green,
modifier = Modifier.padding(10.dp).wrapContentSize()) {
Text(text = "My Dummy Text", color = Color.Black)
}
}
}
}