To acquire proper spacing between sections in a no...
# compose
s
To acquire proper spacing between sections in a non-lazy column, a method I tend to use is to wrap several columns with different vertical arrangements (sample in thread). However, this is not as straight-forward with the APIs provided by the lazy list APIs. What methods do people use to acquire the same result? I would prefer to avoid inter-section value checking to apply different top padding modifiers and stuff, as that code feels less tidy to me.
The non-lazy list approach I tend to use:
Copy code
@Composable
fun EagerListSample(contentPadding: PaddingValues) {
    Column(
        modifier = Modifier
            .verticalScroll(rememberScrollState())
            .padding(contentPadding),
        verticalArrangement = Arrangement.spacedBy(16.dp),
    ) {
        if (favoritesSection.any()) {
            Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                Header("Favorite Items")

                Column {
                    favoritesSection.forEach { item -> Item(item) }
                }
            }
        }

        if (everythingSection.any()) {
            Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                Header("All Items")

                Column {
                    everythingSection.forEach { item -> Item(item) }
                }
            }
        }
    }
}
For lazy lists, the following is technically possible, but I'm not a fan of the modifiers applied to the headers:
Copy code
@Composable
fun LazyListSample(contentPadding: PaddingValues) {
    LazyColumn(contentPadding = contentPadding) {
        if (favoritesSection.any()) {
            item { Header("Favorite Items", Modifier.padding(bottom = 8.dp)) }

            items(favoritesSection) { item -> Item(item) }
        }

        if (everythingSection.any()) {
            item {
                // This feels dirty
                val modifier = if (favoritesSection.any()) Modifier.padding(top = 16.dp) else Modifier

                Header("All Items", modifier.padding(bottom = 8.dp))
            }

            items(everythingSection) { item -> Item(item) }
        }
    }
}
Adding a Spacer is also possible, and gets rid of the conditional modifier, but that feels even worse, as I now have more lazy list items than actual “data items”:
Copy code
@Composable
fun LazyListSample(contentPadding: PaddingValues) {
    LazyColumn(contentPadding = contentPadding) {
        if (favoritesSection.any()) {
            item { Header("Favorite Items", Modifier.padding(bottom = 8.dp)) }

            items(favoritesSection) { item -> Item(item) }
        }

        if (everythingSection.any()) {
            if (favoritesSection.any()) {
                item { Spacer(Modifier.height(16.dp)) }
            }
            
            item { Header("All Items", Modifier.padding(bottom = 8.dp)) }

            items(everythingSection) { item -> Item(item) }
        }
    }
}
What I want might be something like a lazy list grouping API or something, giving me the same nesting flexibility of the non-lazy APIs:
Copy code
fun LazyListScope.group(
    verticalArrangement: Arrangement.Vertical,
    content: LazyListScope.() -> Unit,
) {
    // ...
}

fun LazyListScope.group(
    horizontalArrangement: Arrangement.Horizontal,
    content: LazyListScope.() -> Unit,
) {
    // ...
}
Copy code
@Composable
fun LazyListSample(contentPadding: PaddingValues) {
    LazyColumn(
        contentPadding = contentPadding,
        verticalArrangement = Arrangement.spacedBy(16.dp),
    ) {
        if (favoritesSection.any()) {
            group(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                item { Header("Favorite Items", Modifier.padding(bottom = 8.dp)) }

                group(verticalArrangement = Arrangement.Top) {
                    items(favoritesSection) { item -> Item(item) }
                }
            }
        }

        if (everythingSection.any()) {
            group(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                item { Header("All Items", Modifier.padding(bottom = 8.dp)) }

                group(verticalArrangement = Arrangement.Top) {
                    items(everythingSection) { item -> Item(item) }
                }
            }
        }
    }
}
Alternatively, a simpler API like this would also suffice in my specific use case:
Copy code
fun LazyListScope.spaced(
    itemSpacing: Dp,
    content: LazyListScope.() -> Unit,
) {
    content()
}
Copy code
@Composable
fun LazyListSample(contentPadding: PaddingValues) {
    LazyColumn(contentPadding = contentPadding) {
        spaced(16.dp) {
            if (favoritesSection.any()) {
                spaced(8.dp) {
                    item { Header("Favorite Items", Modifier.padding(bottom = 8.dp)) }

                    spaced(0.dp) {
                        items(favoritesSection) { item -> Item(item) }
                    }
                }
            }

            if (everythingSection.any()) {
                spaced(8.dp) {
                    item { Header("All Items", Modifier.padding(bottom = 8.dp)) }

                    spaced(0.dp) {
                        items(everythingSection) { item -> Item(item) }
                    }
                }
            }
        }
    }
}
However, supporting any arrangement would probably make more sense in a public API.