commonMain.io.github.lyxnx.compose.ui.LazyList.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ui Show documentation
Show all versions of ui Show documentation
Jetpack Compose UI extensions and utilities
package io.github.lyxnx.compose.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import kotlin.jvm.JvmName
/**
* Divides [items] of type [T] within this list using [divider].
*
* For each item, if it is not the last item or [showLastDivider] is true, [divider] will be placed [dividerSpacing]
* away from the mapped [content]. If [isVertical] is true, the divider will be [dividerSpacing] below [content],
* otherwise it will be placed to the end (LTR/RTL direction aware) of the content.
*
* Also for each item, if it is not the first item, it will be [itemSpacing] away from the last.
* For columns ([isVertical] == true), this will be added to the top of the item, otherwise [itemSpacing] will be added
* to the start (LTR/RTL aware).
*
* @param T type of items to display within this list
* @param items items to display within the list
* @param isVertical whether this list is vertical (true for a [LazyColumn], false for a [LazyRow])
* @param divider divider composable to place between each item
* @param itemSpacing amount to space each item from the last
* @param dividerSpacing amount to space the divider from the main content
* @param contentPadding amount to pad each item in addition to the [itemSpacing] (including the divider)
* @param showLastDivider whether to show a divider on the last item
* @param key factory of stable and unique keys representing the item. Using the same key for multiple items in the list
* is not allowed. Type of the key should be saveable via Bundle on Android. If null is passed the position in the list
* will represent the key. When you specify the key the scroll position will be maintained based on the key, which means
* if you add/remove items before the current visible item the item with the given key will be kept as the first visible
* one.
* @param contentType a factory of the content types for the item. The item compositions of the same type could be reused
* more efficiently. Note that null is a valid type and items of such type will be considered compatible.
* @param content mapping function for each item in this list. This takes the current item index and the item itself as
* parameters
* @see LazyListScope.itemsIndexed
*/
public inline fun LazyListScope.dividedItems(
items: List,
isVertical: Boolean,
crossinline divider: @Composable LazyItemScope.() -> Unit,
itemSpacing: Dp,
dividerSpacing: Dp,
contentPadding: PaddingValues = PaddingValues(),
showLastDivider: Boolean = false,
noinline key: ((index: Int, item: T) -> Any)? = null,
crossinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null },
crossinline content: @Composable LazyItemScope.(index: Int, item: T) -> Unit
) {
dividedItems(
items = items,
isVertical = isVertical,
divider = { _, _ -> divider() },
itemSpacing = { _, _ -> itemSpacing },
dividerSpacing = { _, _ -> dividerSpacing },
contentPadding = { _, _ -> contentPadding },
showLastDivider = showLastDivider,
key = key,
contentType = contentType,
content = content
)
}
/**
* Divides [items] of type [T] within this list using [divider].
*
* For each item, if it is not the last item or [showLastDivider] is true, [divider] will be placed [dividerSpacing]
* away from the mapped [content]. If [isVertical] is true, the divider will be [dividerSpacing] below [content],
* otherwise it will be placed to the end (LTR/RTL direction aware) of the content.
*
* Also for each item, if it is not the first item, it will be [itemSpacing] away from the last.
* For columns ([isVertical] == true), this will be added to the top of the item, otherwise [itemSpacing] will be added
* to the start (LTR/RTL aware).
*
* @param T type of items to display within this list
* @param items items to display within the list
* @param isVertical whether this list is vertical (true for a [LazyColumn], false for a [LazyRow])
* @param divider divider composable to place between each item
* @param itemSpacing amount to space each item from the last
* @param dividerSpacing amount to space the divider from the main content
* @param contentPadding amount to pad each item in addition to the [itemSpacing] (including the divider)
* @param showLastDivider whether to show a divider on the last item
* @param key factory of stable and unique keys representing the item. Using the same key for multiple items in the list
* is not allowed. Type of the key should be saveable via Bundle on Android. If null is passed the position in the list
* will represent the key. When you specify the key the scroll position will be maintained based on the key, which means
* if you add/remove items before the current visible item the item with the given key will be kept as the first visible
* one.
* @param contentType a factory of the content types for the item. The item compositions of the same type could be reused
* more efficiently. Note that null is a valid type and items of such type will be considered compatible.
* @param content mapping function for each item in this list. This takes the current item index and the item itself as
* parameters
* @see LazyListScope.itemsIndexed
*/
@JvmName("dividedItemsDp")
public inline fun LazyListScope.dividedItems(
items: List,
isVertical: Boolean,
crossinline divider: @Composable LazyItemScope.(index: Int, item: T) -> Unit,
crossinline itemSpacing: Density.(index: Int, item: T) -> Dp,
crossinline dividerSpacing: Density.(index: Int, item: T) -> Dp,
crossinline contentPadding: Density.(index: Int, item: T) -> PaddingValues = { _, _ -> PaddingValues() },
showLastDivider: Boolean = false,
noinline key: ((index: Int, item: T) -> Any)? = null,
crossinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null },
crossinline content: @Composable LazyItemScope.(index: Int, item: T) -> Unit
) {
dividedItems(
items = items,
isVertical = isVertical,
divider = divider,
itemMargin = { index, item ->
if (isVertical) PaddingValues(top = itemSpacing(index, item))
else PaddingValues(start = itemSpacing(index, item))
},
dividerMargin = { index, item ->
if (isVertical) PaddingValues(top = dividerSpacing(index, item))
else PaddingValues(start = dividerSpacing(index, item))
},
contentPadding = contentPadding,
showLastDivider = showLastDivider,
key = key,
contentType = contentType,
content = content
)
}
/**
* Divides [items] of type [T] within this list using [divider].
*
* For each item, if it is not the last item or [showLastDivider] is true, [divider] will be placed [dividerMargin]
* away from the mapped [content].
*
* Also for each item, if it is not the first item, it will be [itemMargin] away from the last.
*
* Note that most of the parameters are functions that take the index and the item itself as parameters. This allows
* for more dynamic spacing and padding based on the item (or its index). For a more simpler version, use one of the other
* overloads.
*
* @param T type of items to display within this list
* @param items items to display within the list
* @param isVertical whether this list is vertical (true for a [LazyColumn], false for a [LazyRow])
* @param divider divider composable to place between each item
* @param itemMargin amount to space each item from the last
* @param dividerMargin amount to space the divider from the main content
* @param contentPadding amount to pad each item in addition to the [itemMargin] (including the divider)
* @param showLastDivider whether to show a divider on the last item
* @param key factory of stable and unique keys representing the item. Using the same key for multiple items in the list
* is not allowed. Type of the key should be saveable via Bundle on Android. If null is passed the position in the list
* will represent the key. When you specify the key the scroll position will be maintained based on the key, which means
* if you add/remove items before the current visible item the item with the given key will be kept as the first visible
* one.
* @param contentType a factory of the content types for the item. The item compositions of the same type could be reused
* more efficiently. Note that null is a valid type and items of such type will be considered compatible.
* @param content mapping function for each item in this list. This takes the current item index and the item itself as
* parameters
* @see LazyListScope.itemsIndexed
*/
@Suppress("NAME_SHADOWING")
@JvmName("dividerItemsMargin")
public inline fun LazyListScope.dividedItems(
items: List,
isVertical: Boolean,
crossinline divider: @Composable LazyItemScope.(index: Int, item: T) -> Unit,
crossinline itemMargin: Density.(index: Int, item: T) -> PaddingValues,
crossinline dividerMargin: Density.(index: Int, item: T) -> PaddingValues,
crossinline contentPadding: Density.(index: Int, item: T) -> PaddingValues,
showLastDivider: Boolean = false,
noinline key: ((index: Int, item: T) -> Any)? = null,
crossinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null },
crossinline content: @Composable LazyItemScope.(index: Int, item: T) -> Unit
) {
itemsIndexed(items = items, key = key, contentType = contentType) { index, item ->
val density = LocalDensity.current
val itemSpacing = density.itemMargin(index, item)
val contentPadding = density.contentPadding(index, item)
val showDivider = index < items.lastIndex || showLastDivider
if (isVertical) {
Column {
Box(
Modifier
.padding(itemSpacing)
.padding(contentPadding)
) {
content(index, item)
}
if (showDivider) {
Box(Modifier.padding(density.dividerMargin(index, item))) {
divider(index, item)
}
}
}
} else {
// Set intrinsic size on this one as some dividers could use fillMaxHeight and will take up all height
Row(Modifier.height(IntrinsicSize.Min)) {
Box(
Modifier
.padding(itemSpacing)
.padding(contentPadding)
) {
content(index, item)
}
if (showDivider) {
Box(Modifier.padding(density.dividerMargin(index, item))) {
divider(index, item)
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy