androidandroid-jetpack-composeandroid-jetpack-compose-list

Is there a way to align a item on the bottom of a LazyColumn?


I'm trying to implement a list with a button on its bottom, like the following:

enter image description here

Since I don't know how many items I would have on the list, I'm using a LazyColumn. The button on the bottom, should only be placed there if the list doesn't fill the entire screen, if it does, the button should be moved down and be the last item on the list.

Moving the button inside the LazyColumn like this:

LazyColumn(...) {
    items(items) { item -> ...}
    item { Button() } 
}

Gives the following:

enter image description here

I tried to add a Spacer with fillMaxHeight() modifier as an item between the two but it didn't change.

I also tried to add both the LazyColumn and the Button inside a column:

Column {
   LazyColumn(
        modifier = Modifier.weight(1f)
   ) {
        items(items) { item -> ...}
   }
   Button()
}

But this only anchors the button on the bottom as if the Column was a LinearLayout.

Considering this, would it be possible to align one of the LazyColumn's items so it will always be on the bottom? Or, add some kind of space that fill the available area?


Solution

  • As stated in this closed issue it's possible to use LazyColumn's verticalArrangement parameter to align the last item on the bottom by implementing Arrangement.Vertical:

    LazyColumn(verticalArrangement = remember {
        object : Arrangement.Vertical {
            override fun Density.arrange(
                totalSize: Int,
                sizes: IntArray,
                outPositions: IntArray
            ) {
                var currentOffset = 0
                sizes.forEachIndexed { index, size -> 
                    if (index == sizes.lastIndex) {
                        outPositions[index] = totalSize - size
                    } else {
                        outPositions[index] = currentOffset
                        currentOffset += size
                    }
                }
            }
        }
    })
    

    If you want to use it with spacedBy on the verticalArrangement parameter, as I was, you could use the following:

    fun spacedByWithFooter(space: Dp) = object : Arrangement.Vertical {
    
        override val spacing = space
    
        override fun Density.arrange(
            totalSize: Int,
            sizes: IntArray,
            outPositions: IntArray,
        ) {
            if (sizes.isEmpty()) return
            val spacePx = space.roundToPx()
    
            var occupied = 0
            var lastSpace = 0
    
            sizes.forEachIndexed { index, size ->
    
                if (index == sizes.lastIndex) {
                    outPositions[index] = totalSize - size
                } else {
                    outPositions[index] = min(occupied, totalSize - size)
                }
                lastSpace = min(spacePx, totalSize - outPositions[index] - size)
                occupied = outPositions[index] + size + lastSpace
            }
            occupied -= lastSpace
        }
    }