So, my code looks like this (I simplified it a lot for readability)
LazyVerticalGrid(
modifier = modifier
.background(Color.Black)
.padding(16.dp),
cells = GridCells.Fixed(5),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(26) {
Spacer(
modifier = Modifier
.size(64.dp)
.background(Color.Cyan)
)
}
item {
Spacer(
modifier = Modifier
.height(64.dp)
.fillMaxWidth()
.background(Color.Magenta)
)
}
}
And it looks like this (screenshot from preview):
However, when I add a span to the last item (the magenta one):
item(span = { GridItemSpan(2) }) {
//The last item goes here
}
An unwanted space in the bottom is added:
If the item with a span is the first one (before the cyan ones), the space remains in the bottom.
Related
I am trying to make a grid of text items, witch can vary in size, and I would like for as many items to fix in one row as the size allows.
This is what I got so far
For example in this case, Ias you see the first row has space for additional items, while climbing should be Climbing and mountaineering
I have been trying to use LazyVerticalGrid, is there any way to automatically calculate span based on item size?
`
LazyVerticalGrid(columns = GridCells.Adaptive(120.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(14.dp)){
items(selected.size){i->
ItemHobbySelected(text = selected[i])
}
}
#Composable
fun ItemHobbySelected(text: String, onClick: () -> Unit={}){
Box(modifier = Modifier
.wrapContentWidth()
.clip(RoundedCornerShape(8.dp))
.background(Color.White)) {
Text(
modifier = Modifier
.background(Color.Transparent)
.padding(16.dp, 8.dp, 16.dp, 8.dp)
.align(Alignment.Center)
.clickable(onClick = onClick),
text = text,maxLines =1,
color = Color(0xFF3D3D3D)
)
}
}
`
For such designs you can use Google Accompanist's FlowLayout. Add the version based on the Compose version you are using.
implementation "com.google.accompanist:accompanist-flowlayout:<version>"
It has two Composables. FlowRow and FlowColumn. You need FlowRow:
FlowRow(
mainAxisSpacing = 10.dp,
crossAxisSpacing = 10.dp,
modifier = Modifier
.fillMaxWidth()
.padding(4.dp),
mainAxisAlignment = MainAxisAlignment.Center
) {
selected.forEach{ item ->
ItemHobbySelected(text = "//Whatever you want")
}
You can modify the spacings to get your desired layout.
I have create a Column having 5 Rows, Column has vertical scrolling enabled and Rows has horizontal scrolling enabled.
Sometimes when I try to fling or scroll vertically, Rows consume the gesture and stops vertical scroll to happen.
is there a way to enable horizontal scroll only when certain x-delta is dragged/swipped/scrolled?
code to reproduce :
val colors = remember {
listOf(
Color.Blue,
Color.Green,
Color.Cyan,
Color.Magenta
)
}
Box(
modifier = Modifier
) {
Column(
modifier = Modifier
.padding(top = 200.dp)
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
repeat(5) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.horizontalScroll(rememberScrollState())
) {
repeat(5) {
Box(
modifier = Modifier
.size(200.dp)
.background(
color = remember {
colors.random()
}
)
)
}
}
}
}
}
Edit 1: this is a bug in compose issue added to tracker
I have a compose card and inside a row with circle views, I need to make it so the last item in this row is half shown only. Is there a easy way to achieve this without measuring the screen width after everything is drawn and then modify the padding for the row items dynamically to achieve it?)
If you need to calculate the number of elements depending on the size of the cell contents, it is impossible to do this without real measurements.
But if you know exactly how many elements you need to display, you can use Modifier.fillParentMaxWidth with the desired fraction. This is only available in lazy views like LazyRow.
Things are a bit more complicated with spacings: usually contentPadding is used to offset the first element, which reduces the parent size, depending on which Modifier.fillParentPaxMaxWidth calculates the actual value. Also, if you apply it to both start and end, you won't get the desired result. That's why I apply it only to start, and create an equivalent effect at the end with another item.
I also manually surround the Spacers item instead of using Arrangement.spacedBy, because the spacers should be inside the Modifier.fillParentMaxWidth too.
val spacing = 20.dp
val halfSpacing = spacing / 2
val shape = RoundedCornerShape(20)
LazyRow(
contentPadding = PaddingValues(start = halfSpacing),
modifier = Modifier
.padding(30.dp)
.border(1.dp, color = Color.Black, shape = shape)
.clip(shape)
.padding(vertical = 20.dp)
) {
items(10) {
Row(
Modifier
.fillParentMaxWidth(1f / 3.5f)
) {
Spacer(Modifier.size(halfSpacing))
Box(
Modifier
.weight(1f)
.aspectRatio(1f)
.background(Color.Blue, shape = CircleShape)
)
Spacer(Modifier.size(halfSpacing))
}
}
item {
Spacer(Modifier.size(halfSpacing))
}
}
Result:
LazyColumn variant:
val spacing = 20.dp
val halfSpacing = spacing / 2
val shape = RoundedCornerShape(20)
LazyColumn(
contentPadding = PaddingValues(top = halfSpacing),
modifier = Modifier
.padding(30.dp)
.border(1.dp, color = Color.Black, shape = shape)
.clip(shape)
.padding(horizontal = 20.dp)
) {
items(10) {
Column(
Modifier
.fillParentMaxHeight(1f / 3.5f)
) {
Spacer(Modifier.size(halfSpacing))
Box(
Modifier
.weight(1f)
.aspectRatio(1f)
.background(Color.Blue, shape = CircleShape)
)
Spacer(Modifier.size(halfSpacing))
}
}
item {
Spacer(Modifier.size(halfSpacing))
}
}
My PersonalDataBox() doesn't fill max available space even if there is .fillMaxSize()
There is a empty space between my box and the bottom of the screen. I want to fill my whole screen with Box(). It looks like my box has .fillWrapHeight(). Parent of the box is also .fillMaxSize() and it works correctly - fills max height.
#Composable
private fun PersonalDataBox(user: User) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 10.dp)
.clip(RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp))
.background(Color.Red)
.padding(horizontal = 25.dp, vertical = 15.dp)
) {
Column(modifier = Modifier.fillMaxSize()) {
Text(
text = stringResource(R.string.personal_data),
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colors.primary
)
listOf(
Pair(stringResource(R.string.name), user.name),
Pair(stringResource(R.string.surname), user.surname),
Pair(stringResource(R.string.e_mail), user.email),
).forEach {
InputField(it)
}
}
}
}
I pulled down your code and it actually renders in the whole screen just fine--so I'm assuming you have your PersonalDatabox in a compose view with infinite height such as a LazyColumn.
Check out the documentation about constraints here
Specifically:
Most commonly, when measured with unbounded Constraints, these children will fallback to size themselves to wrap their content, instead of expanding to fill the available space (this is not always true as it depends on the child layout model, but is a common behavior for core layout components).
In other words, if the constraint is infinite, then .fillMaxSize will default to wrapping content.
To get around this, you can make the constraint equal to the height of the Lazy Column by using fillParentMaxHeight.
This only works if you're using a LazyColumn, though. Otherwise, if you set the height specifically or measure the screen you can accomplish the same thing.
Here's an example of what that might look like.
LazyColumn() {
item {
Column(modifier = Modifier.fillParentMaxHeight(1f)) {
PersonalDataBox(User(name = "John", surname = "Robless", "coolemail#email.com"))
}
}
}
Your Box has the following modifiers:
modifier = Modifier
.fillMaxSize()
.padding(vertical = 10.dp)
.clip(RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp))
.background(Color.Red)
.padding(horizontal = 25.dp, vertical = 15.dp)
The important ones are
.padding(vertical = 10.dp)
.padding(horizontal = 25.dp, vertical = 15.dp)
There are two padding modifiers adding a total of 25.dp padding to the top and bottom of your Box, remove these and it should fix your issue
I am currently trying to implement a gridview, it consists of 2 columns. I know i can implement my own grid view using columns and rows, but i just want to use existing approach although it is experimental.
#Composable
fun MyGridScreen() {
LazyVerticalGrid(cells = GridCells.Fixed(2), modifier = Modifier.fillMaxSize(),contentPadding = PaddingValues(12.dp)) {
items(15) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(10.dp)
.height(80.dp)
.background(Color.Red)
.aspectRatio(1f)
) {
Text("Grid item $it", color = Color.White)
}
}
}
}
Below is the result i achieved. I can't put space below the item :(
You need to set verticalArrangement and horizontalArrangement properties on the LazyVerticalGrid composable.
This will space the items by 10.dp in both the vertical and horizontal axis.
#Composable
fun MyGridScreen() {
LazyVerticalGrid(
cells = GridCells.Fixed(2),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(12.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp
) {
items(15) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(10.dp)
.height(80.dp)
.background(Color.Red)
.aspectRatio(1f)
) {
Text("Grid item $it", color = Color.White)
}
}
}
}
Padding is exactly what you need in this case. But you need to understand that in compose modifiers order means a lot. By moving background modifier in between padding and sizes modifiers, you'll get what you need. And clickable ripple will work the same.
Check out more how modifiers order works: https://stackoverflow.com/a/65698101/3585796
.padding(10.dp)
.background(Color.Red)
.height(80.dp)
.aspectRatio(1f)
p.s. if you need your items to be a square, just put your box in one more box. Looks like width of items in LazyVerticalGrid gets overridden by the grid to fill max width, not sure if that's a bug or a feature.