CarouselItemDrawInfo

Interface

Common
@ExperimentalMaterial3Api
sealed interface CarouselItemDrawInfo

Interface to hold information about a Carousel item and its size.

Example of CarouselItemDrawInfo usage:

Properties

Common
val size: Float

The size of the carousel item in the main axis in pixels

Common
val minSize: Float

The minimum size of the carousel item in the main axis in pixels, eg. the size of the item as it scrolls off the sides of the carousel

Common
val maxSize: Float

The maximum size of the carousel item in the main axis in pixels, eg. the size of the item when it is at a focal position

Common
val maskRect: Rect

The Rect by which the carousel item is being clipped.

Code Examples

FadingHorizontalMultiBrowseCarouselSample

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun FadingHorizontalMultiBrowseCarouselSample() {
    data class CarouselItem(
        val id: Int,
        @DrawableRes val imageResId: Int,
        @StringRes val contentDescriptionResId: Int,
    )
    val items =
        listOf(
            CarouselItem(0, R.drawable.carousel_image_1, R.string.carousel_image_1_description),
            CarouselItem(1, R.drawable.carousel_image_2, R.string.carousel_image_2_description),
            CarouselItem(2, R.drawable.carousel_image_3, R.string.carousel_image_3_description),
            CarouselItem(3, R.drawable.carousel_image_4, R.string.carousel_image_4_description),
            CarouselItem(4, R.drawable.carousel_image_5, R.string.carousel_image_5_description),
        )
    val state = rememberCarouselState { items.count() }
    HorizontalMultiBrowseCarousel(
        state = state,
        modifier = Modifier.width(412.dp).height(221.dp),
        preferredItemWidth = 186.dp,
        itemSpacing = 8.dp,
        contentPadding = PaddingValues(horizontal = 16.dp),
    ) { i ->
        val item = items[i]
        // For item 1 and 4, create a stacked item layout that clips two images independently
        // to the item's mask
        if (i == 1 || i == 4) {
            Column(
                modifier = Modifier.height(205.dp),
                verticalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                Image(
                    modifier =
                        Modifier.fillMaxWidth()
                            .fillMaxHeight(.5f)
                            .maskClip(MaterialTheme.shapes.extraLarge)
                            .maskBorder(
                                BorderStroke(3.dp, Color.Magenta),
                                MaterialTheme.shapes.extraLarge,
                            ),
                    painter = painterResource(id = item.imageResId),
                    contentDescription = stringResource(item.contentDescriptionResId),
                    contentScale = ContentScale.Crop,
                )
                Image(
                    modifier =
                        Modifier.fillMaxSize()
                            .maskClip(RoundedCornerShape(8.dp))
                            .maskBorder(BorderStroke(5.dp, Color.Green), RoundedCornerShape(8.dp)),
                    painter = painterResource(id = item.imageResId),
                    contentDescription = stringResource(item.contentDescriptionResId),
                    contentScale = ContentScale.Crop,
                )
            }
        } else {
            // Mask using a generic path shape
            val pathShape = remember {
                object : Shape {
                    override fun createOutline(
                        size: Size,
                        layoutDirection: LayoutDirection,
                        density: Density,
                    ): Outline {
                        val roundRect =
                            RoundRect(0f, 0f, size.width, size.height, CornerRadius(30f))
                        val shapePath = Path().apply { addRoundRect(roundRect) }
                        return Outline.Generic(shapePath)
                    }
                }
            }
            Box(
                modifier =
                    Modifier.height(205.dp)
                        .maskClip(pathShape)
                        .maskBorder(BorderStroke(5.dp, Color.Red), pathShape)
            ) {
                Image(
                    painter = painterResource(id = item.imageResId),
                    contentDescription = stringResource(item.contentDescriptionResId),
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Crop,
                )
                ElevatedAssistChip(
                    onClick = { /* Do something! */ },
                    label = { Text("Image $i") },
                    modifier =
                        Modifier.graphicsLayer {
                            // Fade the chip in once the carousel item's size is large enough to
                            // display the entire chip
                            alpha =
                                lerp(
                                    0f,
                                    1f,
                                    max(
                                        size.width - (carouselItemDrawInfo.maxSize) +
                                            carouselItemDrawInfo.size,
                                        0f,
                                    ) / size.width,
                                )
                            // Translate the chip to be pinned to the left side of the item's mask
                            translationX = carouselItemDrawInfo.maskRect.left + 8.dp.toPx()
                        },
                    leadingIcon = {
                        Icon(
                            Icons.Filled.Image,
                            contentDescription = "Localized description",
                            Modifier.size(AssistChipDefaults.IconSize),
                        )
                    },
                )
            }
        }
    }
}