How to make a LazyColumn item occupy the remaining height on Jetpack Compose
Asked Answered
A

1

6

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. sample


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.

sample2

Arsenic answered 2/11, 2022 at 16:5 Comment(2)
You can move the Button outside the LazyColumn. Something like: #73636739Purulence
I updated the sample code to try to make it a bit more clearArsenic
P
2

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()) {
        //..
    }
}

enter image description here

Purulence answered 2/11, 2022 at 16:13 Comment(5)
In my scenario, item content is a default layout that already has a button at bottom, so I trying to use this same layout without break it in more parts. =/Arsenic
@FillipeDuoli Your comment is not clear. In my example the you have an Header and a LazyColumn that occupies only the remaining height available putting the OtherLayout at the Bottom of the screen.Purulence
@FillipeDuoli, I think you should re-think your design or the question perhaps, there's no way you can visibly show the button at the bottom as part of your infinite height scrollable without taking Gabriele's suggestion that makes your LazyColumn occupy the appropriate space of the screen which "breaks your code", or you can perhaps force your LazyColumn to scroll to the bottom.Naumann
@z.y that is the point, if Is possible to do that. I know how to achieve the layout I need changing the structure of the composables. In view system, I can do about the same of this behavior if I add android:fillViewport="true" on ScrollView. @GabrieleMariotti on this example that I did, this OtherLayout is not a footer. My idea is more like a state. I can show the items or this layout, like a error message that has a retry at bottom. I'll add an new example that is about the same but with a footer to see if has another way to try to make my question more clear.Arsenic
@FillipeDuoli "like a error message that has a retry at bottom" - it sounds like a Snackbar, a Toast or a BottomSheetDialog..Naumann

© 2022 - 2024 — McMap. All rights reserved.