I try to achieve this layout but don't really know how:
currently it looks like this:
using this Code:
#Preview(widthDp = 150)
#Composable
fun Item() {
Card(shape = RoundedCornerShape(8.dp)) {
Row {
Box(Modifier.background(Color.Yellow).weight(1f)) {
SomeMoreContentWithUnknownHeight()
}
Box(Modifier.width(20.dp).height(IntrinsicSize.Max).background(Color.Green))
}
}
}
I tried to set the height of the second Box to IntrinsicSize.Max but that didn't change anything. I am currently running Jetpack Compose in Version 1.0.0-beta07
You have to apply Modifier.height(IntrinsicSize.Min) to your Row and .fillMaxHeight() to the 2nd Box.
Something like:
Card(shape = RoundedCornerShape(8.dp)) {
Row(Modifier.height(IntrinsicSize.Min)) {
Box(Modifier
.background(Color.Yellow)
.weight(1f)
) {
//Box(Modifier.height(50.dp))
}
Box(Modifier.width(20.dp)
.fillMaxHeight()
.background(Color.Green)
){
//........
}
}
}
As explained in the doc:
The Row composable's minIntrinsicHeight will be the maximum minIntrinsicHeight of its children. The Green Box element's minIntrinsicHeight is 0 as it doesn't occupy space if no constraints are given; the Yellow Box minIntrinsicHeight will be that of the content given a specific width. Therefore, the Row element's height constraint will be the max minIntrinsicHeight of the Yellow Box content. The Green Box will then expand its height to the height constraint given by the Row.
Related
I'm currently trying to recreate a behavior, upon adding a new element to a LazyColumn the items start shifting to the right, in order to represent a Tree and make the elements easier to read.
The mockup in question:
Documentation
Reading through the documentation of Jetpack Compose in Lists and grids I found the following.
Keep in mind that cases where you’re nesting different direction layouts, for example, a scrollable parent Row and a child LazyColumn, are allowed:
Row(
modifier = Modifier.horizontalScroll(scrollState)
) {
LazyColumn {
// ...
}
}
My implementation
Box(Modifier.padding(start = 10.dp)) {
Row(
modifier = Modifier
.horizontalScroll(scrollState)
.border(border = BorderStroke(1.dp, Color.Black))
) {
LazyColumn(
modifier = Modifier.fillMaxWidth()
) {
for (i in 0..25) {
item {
OptionItem(Modifier.padding(start = (i*20).dp))
}
item {
TaskItem(Modifier.padding(start = (i*10).dp))
}
}
}
}
.
.
.
}
OptionItem represents the element with the dot at the beginning, and TaskItem the other one.
When testing the LazyColumn, it appears as if instead of having a fixed size, the size of the column starts growing just after the elements have gone outside the screen, this causes a strange effect.
As you can see in the GIF, the width of the column starts increasing after the elements no longer fit in the screen.
The Question
I want to prevent this effect from happening, so is there any way I could maintain the width of the column to the maximum all the time?
The reason that applying a simple fillMaxWidth will not work because you are telling a composable to stretch to max, but that is impossible because the view itself can stretch indefinitely since it can be horizontally scrollable. I'm not sure why do you want to prevent this behavior but perhaps maybe you want your views to have some initial width then apply the padding, while maintaining the same width. what you can do in such case is simply give your composables a specific width, or what you can do is to get the width of the box and apply them to your composables by width (i used a text in this case)
val localDensity = LocalDensity.current
var lazyRowWidthDp by remember { mutableStateOf(0.dp) }
Box(
Modifier
.padding(start = 10.dp)
.onGloballyPositioned { layoutCoordinates -> // This function will get called once the layout has been positioned
lazyRowWidthDp =
with(localDensity) { layoutCoordinates.size.width.toDp() } // with Density is required to convert to correct Dp
}
) {
val scrollState = rememberScrollState()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(scrollState)
) {
items(25) { i ->
Text(
text = "Hello",
modifier = Modifier
.padding(start = (i * 20).dp)
.width(lazyRowWidthDp)
.border(1.dp, Color.Green)
)
}
items(25) { i ->
Text(
text = "World",
modifier = Modifier
.padding(start = (i * 10).dp)
.width(lazyRowWidthDp)
.border(1.dp, Color.Green)
)
}
}
}
Edit:
you can apply horizontal scroll to the lazy column itself and it will scroll in both directions
In Jetpack Compose, I have a card composable that I want to be a minimum of 100.dp tall. However, if I use heightIn, the card consumes all available height up until the max. How do I set a minHeight without consuming all height?
Surface(
modifier = heightIn(100.dp, 9999.dp),
) {
// Content
}
To be clear, what I want is the following algorithm:
height = if (contentHeight > minHeight) {
contentHeight
} else {
minHeight
}
Use Modifier.defaultMinSize
Surface(
modifier = Modifier.defaultMinSize(height = 100.dp),
) {
// Content
}
Edit
To be clear, what I want is the following algorithm:
height = if (contentHeight > minHeight) {
contentHeight } else {
minHeight }
Modifier.height(contentHeight.coerceAtLeast(minHeight)) is the shortened algorithm.
When a Layout measures and places children it checks constraints. You can find detail answered here about how it works.
For instance column has minHegith = 100.dp, while maxHeight is your composable height in dp. If it's root it's height of screen in dp
Column(modifier=Modifier.heightIn(min=100.dp)) {
Box(modifier=Modifier.fillMaxSize().background(Color.Yellow))
}
For instance this Box covers whole space because it's min and max constraint is Column's maxHeight. In this case using Modifier.heightIn() won't limit, so you need Modifier.height() such as in my original answer with a fixed value or use custom layout to check and assign .
layout(width, height){} inside Layout.
But there is something to note with Surface that it forces minimum constraints to direct descendant, you can check my answer about Surface here.
Surface(modifier = Modifier.heightIn(min = 100.dp)) {
Box(
modifier = Modifier
.size(300.dp)
.background(Color.Yellow)
)
}
This Box in example will cover all of the height because of being Surface's direct descendant.
Surface(modifier = Modifier.heightIn(min = 100.dp)) {
Column() {
Box(
modifier = Modifier
.size(300.dp)
.background(Color.Yellow)
)
}
}
This one will have 300.dp size while Column covers all available space of Surface
I have the following composable.
#Composable
fun Temp() {
Row(
modifier = Modifier
.background(Color.Red)
.height(IntrinsicSize.Min)
.fillMaxWidth()
) {
Text(text = "Hello", fontSize = 10.sp)
Icon(
imageVector = Icons.Default.Star,
contentDescription = "Star",
modifier = Modifier.fillMaxHeight()
)
}
}
The height of icon is not decreasing from 24.dp. Is there any way I can achieve this behavior. I want the icon size to just be the height of the parent row.
If the text is large. The icons size is increased. I think it has to be with icon minimum size being 24.dp. How can I make icon smaller?
Your code actually works as expected - that's how intrinsic calculations work.
Compose checks the min height of each view and chooses the maximum of those values. In your case, the min height of the image is related to the intrinsic size of the image, which you cannot control in the case of Icons.Default.
A possible solution is to use Modifier.layout. When Compose calculates the intrinsic height, the height constraint will be infinite, in which case you can layout it as a zero size view, so that your text will be the highest. When the intrinsic height is determined, you can measure and position the icon:
Row(
modifier = Modifier
.background(Color.Red)
.height(IntrinsicSize.Min)
.fillMaxWidth()
) {
Text(text = "Hello", fontSize = 10.sp)
Icon(
imageVector = Icons.Default.Star,
contentDescription = null,
modifier = Modifier
.layout { measurable, constraints ->
if (constraints.maxHeight == Constraints.Infinity) {
layout(0, 0) {}
} else {
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
}
}
)
}
Using Modifier.layout you can change size of view and its position. Usually you use it like this:
First parameter, measurable is an object on which you can call measure with constraints - the second layout parameter. measure is gonna calculate the size your view will take, taking constraints in count.
in layout you need to pass the desired view size - usually it can be taken from placeable from the previous step.
inside layout you need to call place on the placeable with the desired offset.
With height(IntrinsicSize.Min) layout content is getting called multiple times:
during the first call(s) max height constraint is equal to Infinity, so intrinsic calculations can select the correct size ignoring the parent size.
In the last call max height constraint is equal to the calculated parent intrinsic height.
In my code during first calls, when height constraint is equal to Infinity, I say that this view has zero size, so it's not counted in intrinsic measurements. When intrinsic height is defined, I can layout it with the final constraints.
This question already has answers here:
Compose: wrap text in Row layout, instead of pushing siblings out
(2 answers)
Closed 11 months ago.
I want to set RadioButton component at the end of Row in Jetpack Compose. Tried to using Constraint Layout and moved RadioButton outside the Row but then the RadioButton wasn't centered with other components in Row. What should I do?
Here is my code:
ConstraintLayout {
val (row, button) = createRefs()
Row(
modifier = Modifier
.height(56.dp)
.fillMaxWidth()
.constrainAs(row){
start.linkTo(parent.start)
end.linkTo(parent.end)
},
verticalAlignment = Alignment.CenterVertically
) {
Icon(
/* *** */
)
Text(
text = "mail#gmail.com",
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
)
RadioButton(
/* *** */
)
}
}
What is more i want to cut the Text component if the text is too long (not overlay or underlay the Radio Button)
The simplest solution would be to add a Spacer with Modifier.weight(1f) between your text and the radio button. Row and Column distribute remaining available space between components with a weight Modifier according to their weight. Since there is only one, it will receive all of the remaining space, pushing the radio button to the far right.
For example following code would produce your desired behavior:
Row(modifier = Modifier.height(56.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically){
Icon(Icons.Default.Add,null)
Text("Some text here")
Spacer(Modifier.weight(1f).fillMaxHeight().background(Color.Green)) // height and background only for demonstration
RadioButton(selected = false, onClick = { /*TODO*/ })
}
As I said remaining space is distributed according to the weight of each element, so although this is not what you want to achieve, an example how this might look like
Row(modifier = Modifier.height(56.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Add, null)
Spacer(Modifier.weight(1f).fillMaxHeight().background(Color.Red)) // height and background only for demonstration
Text("Some text here")
Spacer(Modifier.weight(4f).fillMaxHeight().background(Color.Green)) // height and background only for demonstration
RadioButton(selected = false, onClick = { /*TODO*/ })
}
will get you
The remaining space after measuring the icon, text and radio button are distributed as 20% to the red Spacer and 80% to the green one, since that is their share of the total weight (1/5 and 4/5)
You could create a Box that fills the rest of the Row and put the Button inside it. You can then align the Button to the right.
Box(modifier = Modifier.fillMaxWidth()) {
RadioButton(modifier = Modifier.align(Alignment.End)){}
}
The same can be achieved with columns instead of the Box, but then every separate element inside the row should have a column wrapping it.
Here's a test composable:
#Preview
#Composable
fun SliderInRow() {
Row {
Text("qwe")
Slider(value = 0f, onValueChange = {})
Text("wer")
}
}
I want a row with a text, a slider and another text. However, the last text composable (wer) is missed:
What am I doing wrong?
Slider is designed to take all width available, same as any view with Modifier.fillMaxWidth does.
That's why he compresses the last view and makes it zero width.
The recommended way to create such a layout is to add Modifier.weight(1f) to the flexible view, Slider in you case. From its documentation:
The parent will divide the horizontal space remaining after measuring unweighted child elements and distribute it according to this weight.
Row {
Text("qwe")
Slider(value = 0f, onValueChange = {}, modifier = Modifier.weight(1f))
Text("wer")
}