Column(LayoutSize.Fill) {
Box(
modifier = LayoutSize(20.dp) + LayoutSize.Min(40.dp, 40.dp) + LayoutAlign.TopCenter,
backgroundColor = Color.Blue
)
Box(
LayoutSize(50.dp) + LayoutSize.Fill + LayoutAlign.CenterVertically,
backgroundColor = Color.Blue
)
}
I try this sample LayoutAlign
but no rectangles are shown
Quick Answer
This sample is broken due to the wrong order of modifiers. Order of modifiers is very important in Compose. This code would work as expected:
Box(
LayoutSize.Min(40.dp, 40.dp) + LayoutAlign.TopCenter + LayoutSize(20.dp),
backgroundColor = Color.Blue
)
Why it doesn't work in the original sample?
LayoutAlign aligns the content within bounds, when content size is smaller than its bounds. What it does under the hood is resetting min size constraint (minWidth, minHeight, or both, depending on alignment direction) allowing the content to be smaller and occupy its preferred size.
Box with just background color doesn't provide any preferred intrinsic size of its content. If it's allowed to be as small as 0dp x 0dp - it will be.
In the correct example, this is how constraints are modified under the hood:
LayoutSize.Min(40.dp, 40.dp) reserves at least 40dp bounds
Contraints: minWidth = 40dp, maxWidth = infinity, minHeight = 40dp, maxHeight = infinity
LayoutAlign.TopCenter applies alignment and resets min size constraints for both directions
Constraints: minWidth = 0dp, maxWidth = infinity, minHeight = 0dp, maxHeight = infinity
LayoutSize(20.dp) orders the content to be exactly 20dp big. minWidth and minHeight are important here, without it the content of the Box would be measured as 0dp x 0xp.
Constraints: minWidth = 20dp, maxWidth = 20dp, minHeight = 20dp, maxHeight = 20dp.
Without the last step the Box content will be measured as 0dp x 0dp. This is what's happening in the original sample. The box is technically aligned/positioned correctly within 40dp, but it's invisible because its size is 0dp x 0dp.
Using LayoutAlign on elements that provide its own preferred size
Keep in mind that if the element knows how to measure its content or provides some preferred size, then it will work just fine even if we won't set any size after applying the LayoutAlign modifier.
Example:
Text("Text",
modifier = LayoutSize.Min(40.dp, 40.dp) + LayoutAlign.TopCenter,
style = TextStyle(fontSize = 8.sp)
)
Related
I'm unable to set the maximum width of the bottom sheet in the ModalBottomSheetLayout. I've tried the following but that changes the entire page's maximum width, not just the bottom sheet:
ModalBottomSheetLayout(
modifier = Modifier.requiredWidthIn(max = 640.dp),
... // rest of code not shown
The Material specs say to set a maximum width of 640dp for bottom sheets. How do we comply with this requirement?
I filed a bug with Google but wasn't sure if anyone has insight on how to do this.
You should use Modifier.fillMaxWidth().
Moreover you can find out max width of screen by using BoxWithConstraint
Example:
BoxWithConstraints {
val widthModifier = if (maxWidth < 400.dp) {
Modifier.fillMaxWidth()
} else {
Modifier.width(640.dp)
}
ModalBottomSheetLayout(
modifier = *yourModifier*.then(widthModifier),
...
}
This code fills the full screen if i specify the size to be 100.dp.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
This code behave properly by filling the required size.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
}
Can somebody please explain why is it happening?
This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.
As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.
Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.
Here is how one of the maintainers explains the reasons for this decision:
Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.
It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.
This code fills the full screen if i specify the size to be 100.dp.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
This code behave properly by filling the required size.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
}
Can somebody please explain why is it happening?
This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.
As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.
Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.
Here is how one of the maintainers explains the reasons for this decision:
Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.
It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.
I'm looking to have a LazyColumn that initially wraps content height, but if it gets to a certain height (232dp) then it fixes its height at this. In XML I would have used maxHeight combined with wrapContent, but I cant find anything similar in Compose?
You can use hightIn modifier on LazyColumn like this
LazyColumn(
modifier = Modifier
.heightIn(0.dp, 232.dp) //mention max height here
.widthIn(0.dp, 232.dp) //mention max width here
) {
}
This will make sure that the height will not go above 232.dp if more items are added to lazycolumn
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