Home > Mobile >  Make last Item of the Compose LazyColumn fill rest of the screen
Make last Item of the Compose LazyColumn fill rest of the screen

Time:07-12

There is a regular list with some data and LazyColumn for showing it.

LazyColumn {
    items (list) { item ->
        ListItem(item)
    }
}

Simplified ListItem looks like:

@Composable
fun ListItem(item: SomeItem) {
    Row(
       modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Min)
    ) {
        //Some widgets
    }
}

I need to set the footer's item (last in list) height to fill screen if there is not enough items for that. It's easy to find last item and provide some flag to ListItem(item: SomeItem, isLast: Boolean), but I don't know how to set the item's height to achieve my goal. Did anyone faced this problem?

It's almost the same question as was asked here about enter image description here

CodePudding user response:

If your items are all same height and you have have info of how tall they are via static height or using Modifier.onSizeChanged{} you can do this by getting height of the LazyColumn using BoxWithConstraints maxHeight or rememberLazyListState().layoutInfo.viewportSize

@Composable
private fun ListComposable() {

    val myItems = mutableListOf<String>()
    repeat(4) {
        myItems.add("Item $it")
    }


    BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize()
                .border(2.dp, Color.Cyan)
        ) {
            itemsIndexed(myItems) { index: Int, item: String ->

                if (index == myItems.size - 1) {
                    Text(
                        text = item,
                        modifier = Modifier
                            .fillMaxWidth()
                            .height((maxHeight - 50.dp * (myItems.size - 1)).coerceAtLeast(50.dp))
                            .background(Color.Green)
                    )
                } else {
                    Text(
                        text = item,
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(50.dp)
                            .background(Color.Red)
                    )
                }
            }
        }
    }
}

If you don't know total height until last item you will need to use SubcomposeLayout with list that doesn't contain last item and pass LazyColumn height- (items.size-1) total height as your last item's height.

@Composable
fun DimensionMeasureSubcomposeLayout(
    modifier: Modifier = Modifier,
    mainContent: @Composable () -> Unit,
    dependentContent: @Composable (IntSize, Constraints) -> Unit
) {

    SubcomposeLayout(modifier = modifier) { constraints ->

        // Subcompose(compose only a section) main content and get Placeable
        val mainPlaceables: List<Placeable> = subcompose(SlotsEnum.Main, mainContent)
            .map {
                it.measure(constraints)
            }

        // Get max width and height of main component
        var maxWidth = 0
        var maxHeight = 0

        mainPlaceables.forEach { placeable: Placeable ->
            maxWidth  = placeable.width
            maxHeight = placeable.height
        }

        val maxSize = IntSize(maxWidth, maxHeight)

        val dependentPlaceables = subcompose(SlotsEnum.Dependent) {
            dependentContent(maxSize, constraints)
        }.map {
            it.measure(constraints)
        }

        layout(constraints.maxWidth, constraints.maxHeight) {
            dependentPlaceables.forEach { placeable: Placeable ->
                placeable.placeRelative(0, 0)
            }
        }
    }
}

enum class SlotsEnum { Main, Dependent }

And use it as

@Composable
private fun SubcomposeExample() {
    val myItems = mutableListOf<String>()
    repeat(15) {
        myItems.add("Item $it")
    }

    val subList = myItems.subList(0, myItems.size - 1)
    val lasItem = myItems.last()

    val density = LocalDensity.current

    DimensionMeasureSubcomposeLayout(
        modifier = Modifier,
        mainContent = {
            LazyColumn(
                modifier = Modifier.fillMaxWidth()
            ) {
                items(subList) { item: String ->
                    Text(
                        text = item,
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(50.dp)
                            .background(Color.Red)
                    )
                }
            }
        },
        dependentContent = { intSize, constraints ->

            val lastItemHeight = with(density) {
                (constraints.maxHeight - intSize.height).toDp().coerceAtLeast(50.dp)
            }
            LazyColumn(modifier = Modifier.fillMaxWidth()) {
                if (myItems.size > 1) {

                    items(subList) { item: String ->
                        Text(
                            text = item,
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(50.dp)
                                .background(Color.Red)
                        )
                    }
                }

                item {
                    Text(
                        text = lasItem,
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(lastItemHeight)
                            .background(Color.Green)
                    )
                }
            }
        }
    )
}
  • Related