When trying to put a LazyVerticalGrid inside a scrollable Column I get the following error:
java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like LazyColumn and Column(Modifier.verticalScroll()) is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().
I am not making a traditional list, I just have alot of elements that are too big to fit on the screen. Therefore I want the column to scroll so I can see all the elements. Here is my code:
you need to use an item with a span attribute. Assume that your grid view has three items in every row, you can put your item wherever you want with the scrollable screen.
LazyVerticalGrid(cells = GridCells.Fixed(4)) {
item(span = { GridItemSpan(4) }) { Header() }
item(span = { GridItemSpan(4) }) { AnotherView() }
items(gridItems) { GridItemView(it) }
}
If you want to keep some UI elements sticky at the top or bottom, you can play with the Modifier. weight(), for example:
#Composable
fun SomeScreenContent() {
val gridItems = List(30){ it.toString() }
Column(modifier = Modifier.fillMaxSize()) {
SomeTopContent(modifier = Modifier.weight(0.2f))
LazyVerticalGrid(
modifier = Modifier.weight(0.6f),
columns = GridCells.Fixed(4),
contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(gridItems) { item ->
GridItem(item)
}
}
SomeBottomContent(modifier = Modifier.weight(0.2f))
}
}
#Composable
fun SomeTopContent(modifier:Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.background(Color(0xff5077FC)),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(text = "Top")
}
}
#Composable
fun SomeBottomContent(modifier:Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.background(Color(0xff4EE180)),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(text = "Bottom")
}
}
#Composable
fun GridItem(item: String) {
Card(modifier = Modifier.size(100.dp)) {
Text(text = item)
}
}
Or just use the Scaffold composable function...
Related
I'm trying to create a list with customized text views, like the image bellow.
I tried to do that with a lazy column, but it does not work properly.
The problem is that row does not grow with the content created, I don't know how compose redraw that view behind de hood but it isn't working to me.
#Composable
fun NumberList(viewModel: HomeViewModel) {
val gameList = viewModel.numberList //The list of numbers
LazyColumn(
modifier = Modifier.padding(top = 24.dp),
content = {
item {
gameList.forEach {
MegaCard(item = it)
}
}
})
}
#Composable
fun MegaCard(item: String) {
val itemList = item.split("-")
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(horizontal = 16.dp, vertical = 4.dp)
) {
Row(modifier = Modifier.padding(3.dp)) {
itemList.forEach { number ->
GameCircle(number)
}
}
}
}
#Composable
fun GameCircle(number: String) {
val megaColor = colorResource(id = R.color.mega_green)
Text(
modifier = Modifier
.padding(16.dp)
.drawBehind {
drawCircle(
color = megaColor,
radius = this.size.maxDimension
)
},
text = number,
style = MaterialTheme.typography.body2,
color = Color.White,
fontSize = 18.sp
)
}
The final result is something like this bellow:
As more game numbers are added, I assume you want each row to grow vertically. If that's the case, the problem is that the total circle size exceeds the available space. You can use a FlowRow for this:
#Composable
fun MegaCard(item: String) {
val itemList = item.split("-")
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(horizontal = 16.dp, vertical = 4.dp)
) {
FlowRow(modifier = Modifier.padding(3.dp)) {
itemList.forEach { number ->
GameCircle(number)
}
}
}
}
You'll need to add the flow layout Gradle dependency:
implementation 'com.google.accompanist:accompanist-flowlayout:0.25.1'
Also, unless you intend to treat them as one entity, I recommend not adding every row into a single item. The docs go into more detail on the pitfalls of doing so. Here's the final result using FlowRow.
Can I make a item on LazyColumn occupy only the remaining height available? I'm I tried to use fillParentMaxSize but it make the item as the same size of the LazyColumn, so i can't put another item at the top of the list, like a header that I want to scroll with the content.
Sample code
#Composable
fun LazyColumnTest() {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
Column {
Text(text = "This is a title", style = MaterialTheme.typography.h4)
Text(text = "With a subtitle", style = MaterialTheme.typography.subtitle1)
}
}
item {
OtherLayout(modifier = Modifier.fillParentMaxHeight())
}
}
}
#Composable
fun OtherLayout(modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
Icon(
Icons.Default.Close,
contentDescription = null,
modifier = Modifier
.size(150.dp)
.align(Alignment.TopCenter)
)
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.padding(bottom = 16.dp)
.align(Alignment.BottomCenter)
) {
Text(text = "Button at bottom")
}
}
}
Here is the current result. The button is outside the screen, so I need to scroll to see it.
Update
In the example above, the idea is to use this OtherLayout like a state. I can show the items or this OtherLayout that has a button at bottom, like a retry button.
I can do about the same layout on view system if I add fillViewport="true" on a ScrollView. It's possible to add a gravity="bottom" on another view and it will stay at the bottom of screen.
I will add a new example here with the header/footer layout to see if I can explain better.
#Composable
fun LazyColumnTest() {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
// need to scroll with the content
Header()
}
items(2) { position ->
Text(text = "Item $position")
}
item {
// need to stay at bottom if the items not fill the screen
// and scroll with the content if has a lot of items
Footer(modifier = Modifier)
}
}
}
#Composable
private fun Header() {
Column {
Text(text = "This is a title", style = MaterialTheme.typography.h4)
Text(text = "With a subtitle", style = MaterialTheme.typography.subtitle1)
}
}
#Composable
fun Footer(modifier: Modifier = Modifier) {
Column(modifier = modifier.fillMaxWidth()) {
Text(text = "This is a default footer that cannot be changed")
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.padding(bottom = 16.dp)
) {
Text(text = "With a button")
}
}
}
in this example I need that header and footer scrolls with the content, but if the items not fill the entire screen, the footer remains at bottom of the screen.
Using the item function you are adding another item to the scrollable content of the LazyColumn.
If you want that the LazyColumn occupies only the remaining height available, and the footer that doesn't scroll with the list, you can move the footer out of the LazyColumn and apply the weight modifier to the LazyColumn.
Something like:
Column(){
Header()
LazyColumn(Modifier.weight(1f)) {
//....
}
Footer() //your Box
}
In your case:
Column {
//Header
Box(Modifier.fillMaxWidth().height(30.dp).background(Red))
LazyColumn(Modifier.weight(1f)) {
items(itemsList){
Text("Item $it")
}
}
//Footer
OtherLayout()
}
With:
#Composable
fun OtherLayout(modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxWidth()) {
//..
}
}
How to add a lazy column inside another lazy column in jetpack compose like below screenshot.(The inner list size maybe vary)
#Composable
fun SingleItem() {
LazyColumn(modifier = Modifier
.fillMaxWidth()
),
) {
item {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.ic_school),
contentScale = ContentScale.Crop,
contentDescription = null
)
Text(
text = "School",
modifier = Modifier
.fillMaxWidth()
.weight(1f)
)
Icon(
painter = painterResource(id = R.drawable.ic_down_arrow),
modifier = Modifier
.size(dimensionResource(id = R.dimen.margin_large))
.clip(CircleShape)
.clickable { isExpand = !isExpand },
contentDescription = stringResource(id = R.string.expand_list)
)
}
}
if (isExpand) {
item {
Divider(
modifier = Modifier
.padding(vertical = dimensionResource(id = R.dimen.margin_large))
.background(DividerColor.copy(alpha = 0.5F))
.fillMaxWidth(), thickness = 0.5.dp
)
}
items(3) {
Row(modifier = Modifier
.padding(horizontal = dimensionResource(id = R.dimen.margin_large))
.fillMaxWidth()) {
Text("School",
modifier = Modifier
.fillMaxWidth()
.weight(1f),
)
Text(text = "3 KM",
textAlign = TextAlign.End
)
}
}
}
}
}
//For Full list
#Composable
fun MainList()
{
LazyColumn() {
items(10) {
SingleItem()
}
}
}
But using this code it shows following error.
Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed. One of the common reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a header before the list of items please add a header as a separate item() before the main items() inside the LazyColumn scope. There are could be other reasons for this to happen: your ComposeView was added into a LinearLayout with some weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a custom layout. Please try to remove the source of infinite constraints in the hierarchy above the scrolling container.
It is recommended to avoid Nested Scrolling in the same direction. One possible hack if you already know the size of the component you can use that in modifier.
or try wrapping all of your composables inside one parent LazyColumn and using its DSL to pass in different types of content.
In your example
#Composable
fun MainList()
{
LazyColumn() {
items(10) { item ->
ListComposable(item)
}
}
}
#Composable
private fun ListComposable(item: List<String>){
//...
}
This item can also be a multiple list item.
Reference: https://developer.android.com/jetpack/compose/lists#avoid-nesting-scrollable
I want multiple All LazyColumn scroll simultaneously
I can't use LazyHorizontalGrid or LazyVerticalGrid because of inner layout what two layout is different.
How can i share scrolling in multiple LazyColumn?
#Composable
fun TableScreen2(list: List<Time>, cal: Calendar, df: DateFormat) {
LazyRow(Modifier.fillMaxSize()) {
item {
LazyColumn(
modifier = Modifier
.fillParentMaxHeight()
) {
items(count = list.first().timeList.size / 2) {
Column(
modifier = Modifier
.width(60.dp)
.height(50.dp),
verticalArrangement = Arrangement.Top
) {
Text(df.format(stateCal.time))
stateCal.add(Calendar.MINUTE, 30)
}
}
}
}
items(4) { listIndex ->
LazyColumn(modifier = Modifier.fillParentMaxHeight()) {
itemsIndexed(list[listIndex].timeList) { timeIndex, timeItem ->
Box(
modifier = Modifier
.height(30.dp)
.width(60.dp)
.background(Color.Gray),
contentAlignment = Alignment.Center
) {
Text(text = "$timeIndex")
}
}
}
}
}
}
here is gif
A single LazyColumn, where each entry in the lazy column is a row of 5 items looks like it would fit what you want.
I am trying to work with the compose lazy column, i want each item of my lazy column to have the maximum height , I tried the below code but it is not giving the expected result
Code
val itemsList: List by mainScreenViewModel.portfolioItems.observeAsState(listOf())
Box(modifier = Modifier.fillMaxSize()) {
Column(modifier = Modifier.matchParentSize()) {
LazyColumn(
content = {
itemsIndexed(itemsList) { index, item ->
Text(text = "Hello", modifier = Modifier.fillMaxHeight())
}
},
)
}
}
fillMaxHeight()/fillMaxWidth() do not work correctly for vertical and horizontal scrolling list respectively. Instead we can use fillParentHeight() and fillParentWidth() respectively provided in LazyItemScope.
Sample Code:
LazyColumn(
modifier = modifier.fillMaxHeight()) {
items(count = 3) {
Column(modifier = Modifier.fillParentMaxHeight()) {
Text(
text = "Item $it",
modifier = Modifier.fillMaxWidth()
)
Divider(Modifier.height(2.dp))
}
}
}
Refer for more details: https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/LazyItemScope