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
Related
Below is a simplified version of the bar from the diagram that I am implementing. While scrolling the chart, I change its scale and would like to animate it, but during the animation, the edge of the column moves away from the edge of the chart. How can I avoid this?
demo
#Composable
fun AnimationTest() {
var toggle by remember { mutableStateOf(true) }
Row(
modifier = Modifier
.padding(8.dp)
.animateContentSize()
.clickable { toggle = !toggle },
) {
Spacer(modifier = Modifier.weight(if (toggle) 0.4f else 0.6f))
Card(
modifier = Modifier
.animateContentSize()
.weight(if (toggle) 0.6f else 0.4f)
) {
Row {
Box(
modifier = Modifier
.weight(if (toggle) 0.2f else 0.1f)
.background(Color.Magenta)
.height(56.dp)
)
Box(
modifier = Modifier
.weight(if (toggle) 0.4f else 0.3f)
.background(Color.Blue)
.height(56.dp)
)
}
}
}
}
I tried to use Animated Content, but I don't quite understand what to specify as transitionSpec.
I guess the problem is that the Card to which I apply the animateContentSize() also changes its size, but not by changing the size of children, but by changing its own weight.
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.
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.
I'd like to test a simple layout in compose:
A ConstraintLayout (yellow) wrapping
StickyTopText (green)
a Scrolling View (gray)
StickyBottomText (yellow)
I implemented it like this:
#Composable
#Preview
fun MapOverlay() {
ConstraintLayout(
modifier = Modifier
.background(Color.Yellow)
.fillMaxHeight()
) {
val (stickyTop, scroller, stickyBottom) = createRefs()
Text(text = "Sticky Top Text",
modifier = Modifier
.constrainAs(stickyTop) {
top.linkTo(parent.top)
}
.background(Color.Green)
)
Column(
modifier = Modifier
.constrainAs(scroller) {
top.linkTo(stickyTop.bottom)
bottom.linkTo(stickyBottom.top)
height = Dimension.fillToConstraints
}
.verticalScroll(rememberScrollState())
) {
repeat(80) {
Text(
text = "This is Test $it of 80",
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
)
}
}
Text(text = "Sticky Bottom Text",
modifier = Modifier
.background(Color.Red)
.constrainAs(stickyBottom) {
bottom.linkTo(parent.bottom)
})
}
}
Most of it works pretty fine, except the list getting cut off at the end at item 77 instead of 80: (79; zero-Indexed)
What am i doing wrong? Or is this a bug?
(I know i might do this via a scaffold, but that seemed over engineered. Also i would like to understand the issue, not circumvent it
Compose version 1.0.0-beta09
I checked your code and it's really not working on 1.0.0-alpha07, but it's working on 1.0.0-alpha08 (and compose 1.0.0-beta09) 😉
Also, is there any particular reason you're using ConstraintLayout? You can achieve the same result using this code:
Column(
Modifier
.background(Color.Yellow)
.fillMaxHeight()
) {
Text("Sticky Top Text", Modifier.background(Color.Green))
Column(
Modifier
.weight(1f)
.verticalScroll(rememberScrollState())
) {
repeat(80) {
Text(
"This is Test ${it + 1} of 80",
Modifier
.fillMaxWidth()
.background(Color.LightGray)
)
}
}
Text("Sticky Bottom Text", Modifier.background(Color.Red))
}
Is there something like ScrollView fillViewPort in Jetpack Compose Column?
See this example:
#Composable
fun FillViewPortIssue() {
Column(
Modifier
.fillMaxSize()
.padding(16.dp)
) {
for (i in 0..5) {
Box(
modifier = Modifier
.padding(vertical = 8.dp)
.background(Color.Red)
.fillMaxWidth()
.height(72.dp)
)
}
Spacer(modifier = Modifier.weight(1f))
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { /*TODO*/ }
) {
Text("Ok")
}
}
}
This is the result:
When the device is in landscape, the content is cropped, because there's no scroll.
If I add the verticalScroll modifier do the Column...
...
Column(
Modifier
.verticalScroll(rememberScrollState()) // <<-- this
.fillMaxSize()
.padding(16.dp)
) {
...
... the scroll problem is fixed, but the button goes up, like this.
In the traditional toolkit, we can fix this using ScrollView + fillViewPort property. Is there something equivalent to Compose?
Just change the order of the modifiers worked...
Thanks to Abhishek Dewan (from Kotlin Slack channel)!
Column(
Modifier
.fillMaxSize() // first, set the max size
.verticalScroll(rememberScrollState()) // then set the scroll
) {
I had a similar issue recently and substituted verticalScroll for scrollable(rememberScrollState(), Orientation.Vertical) which seemed to work for my case and allow the screen to fill its viewport while allowing scrolling