HorizontalMultiBrowseCarousel

A horizontal carousel meant to display many items at once for quick browsing of smaller content like album art or photo thumbnails.

RevenueCat

RevenueCat

Add subscriptions to your apps in minutes

Ad Get started for free

CarouselWithShowAllButtonSample

@Preview
@Composable
fun CarouselWithShowAllButtonSample() {
    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),
        )
    var showAllItems by remember { mutableStateOf(false) }
    BackHandler(enabled = showAllItems) { showAllItems = false }
    if (showAllItems) {
        // Shows the grid page directly. For better user experience and navigation patterns,
        // consider navigating to a dedicated screen
        LazyVerticalGrid(
            columns = GridCells.Fixed(2),
            contentPadding = PaddingValues(16.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp),
            horizontalArrangement = Arrangement.spacedBy(8.dp),
            modifier = Modifier.fillMaxSize().padding(top = 8.dp),
        ) {
            items(items.size) { i ->
                val item = items[i]
                Image(
                    painter = painterResource(id = item.imageResId),
                    contentDescription = stringResource(item.contentDescriptionResId),
                    modifier = Modifier.height(205.dp).clip(MaterialTheme.shapes.extraLarge),
                    contentScale = ContentScale.Crop,
                )
            }
        }
    } else {
        Column(
            modifier =
                Modifier.fillMaxSize()
                    .verticalScroll(rememberScrollState())
                    .padding(vertical = 16.dp)
        ) {
            HorizontalMultiBrowseCarousel(
                state = rememberCarouselState { items.count() },
                modifier = Modifier.width(412.dp).height(221.dp),
                preferredItemWidth = 186.dp,
                itemSpacing = 8.dp,
                contentPadding = PaddingValues(horizontal = 16.dp),
            ) { i ->
                val item = items[i]
                Image(
                    modifier = Modifier.height(205.dp).maskClip(MaterialTheme.shapes.extraLarge),
                    painter = painterResource(id = item.imageResId),
                    contentDescription = stringResource(item.contentDescriptionResId),
                    contentScale = ContentScale.Crop,
                )
            }
            TextButton(
                onClick = { showAllItems = true },
                modifier =
                    Modifier.align(Alignment.End).padding(vertical = 4.dp, horizontal = 16.dp),
            ) {
                Text("Show all")
            }
            // An example of the content for the scrollable page
            (0..4).forEach { index ->
                Spacer(modifier = Modifier.height(16.dp))
                Box(
                    modifier =
                        Modifier.fillMaxWidth()
                            .height(140.dp)
                            .padding(horizontal = 16.dp)
                            .background(
                                MaterialTheme.colorScheme.surfaceVariant,
                                RoundedCornerShape(16.dp),
                            )
                ) {}
            }
        }
    }
}

HorizontalMultiBrowseCarouselSample

@Preview
@Composable
fun HorizontalMultiBrowseCarouselSample() {
    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() }
    val animationScope = rememberCoroutineScope()
    HorizontalMultiBrowseCarousel(
        state = state,
        modifier = Modifier.fillMaxWidth().height(221.dp),
        preferredItemWidth = 186.dp,
        itemSpacing = 8.dp,
        contentPadding = PaddingValues(horizontal = 16.dp),
    ) { i ->
        val item = items[i]
        Image(
            modifier =
                Modifier.height(205.dp)
                    .fillMaxWidth()
                    .clickable(true, "Tap to focus", Role.Image) {
                        animationScope.launch { state.animateScrollToItem(i) }
                    }
                    .maskClip(MaterialTheme.shapes.extraLarge),
            painter = painterResource(id = item.imageResId),
            contentDescription = stringResource(item.contentDescriptionResId),
            contentScale = ContentScale.Crop,
        )
    }
}