HorizontalMultiBrowseCarousel

Functions summary

Unit
@Composable
HorizontalMultiBrowseCarousel(
    state: CarouselState,
    preferredItemWidth: Dp,
    modifier: Modifier,
    itemSpacing: Dp,
    flingBehavior: TargetedFlingBehavior,
    userScrollEnabled: Boolean,
    minSmallItemWidth: Dp,
    maxSmallItemWidth: Dp,
    contentPadding: PaddingValues,
    content: @Composable CarouselItemScope.(itemIndex: Int) -> Unit
)

Material Design Carousel

Cmn

Functions

HorizontalMultiBrowseCarousel

@Composable
fun HorizontalMultiBrowseCarousel(
    state: CarouselState,
    preferredItemWidth: Dp,
    modifier: Modifier = Modifier,
    itemSpacing: Dp = 0.dp,
    flingBehavior: TargetedFlingBehavior = CarouselDefaults.singleAdvanceFlingBehavior(state = state),
    userScrollEnabled: Boolean = true,
    minSmallItemWidth: Dp = CarouselDefaults.MinSmallItemSize,
    maxSmallItemWidth: Dp = CarouselDefaults.MaxSmallItemSize,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    content: @Composable CarouselItemScope.(itemIndex: Int) -> Unit
): Unit

Material Design Carousel

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

Note that this carousel may adjust the size of items in order to ensure a mix of large, medium, and small items fit perfectly into the available space and are arranged in a visually pleasing way. Carousel then lays out items using the large item size and clips (or masks) items depending on their scroll offset to create items which smoothly expand and collapse between the large, medium, and small sizes.

For more information, see design guidelines

Example of a multi-browse carousel:

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.icons.filled.Image
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.carousel.HorizontalMultiBrowseCarousel
import androidx.compose.material3.carousel.rememberCarouselState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp

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,
    )
}
import androidx.activity.compose.BackHandler
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.filled.Image
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.carousel.HorizontalMultiBrowseCarousel
import androidx.compose.material3.carousel.rememberCarouselState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp

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),
                        )
            ) {}
        }
    }
}
Parameters
state: CarouselState

The state object to be used to control the carousel's state

preferredItemWidth: Dp

The width that large, fully visible items would like to be in the horizontal axis. This width is a target and will likely be adjusted by carousel in order to fit a whole number of items within the container. Carousel adjusts small items first (between the minSmallItemWidth and maxSmallItemWidth). Then medium items, when present, are adjusted to use a width anywhere between the small item width and large item width. Finally, large items are adjusted if necessary.

modifier: Modifier = Modifier

A modifier instance to be applied to this carousel container

itemSpacing: Dp = 0.dp

The amount of space used to separate items in the carousel

flingBehavior: TargetedFlingBehavior = CarouselDefaults.singleAdvanceFlingBehavior(state = state)

The TargetedFlingBehavior to be used for post scroll gestures

userScrollEnabled: Boolean = true

whether the scrolling via the user gestures or accessibility actions is allowed.

minSmallItemWidth: Dp = CarouselDefaults.MinSmallItemSize

The minimum allowable width of small items in dp. Depending on the preferredItemWidth and the width of the carousel, the small item width will be chosen from a range of minSmallItemWidth and maxSmallItemWidth

maxSmallItemWidth: Dp = CarouselDefaults.MaxSmallItemSize

The maximum allowable width of small items in dp. Depending on the preferredItemWidth and the width of the carousel, the small item width will be chosen from a range of minSmallItemWidth and maxSmallItemWidth

contentPadding: PaddingValues = PaddingValues(0.dp)

a padding around the whole content. This will add padding for the content after it has been clipped. You can use it to add a padding before the first item or after the last one. Use itemSpacing to add spacing between the items.

content: @Composable CarouselItemScope.(itemIndex: Int) -> Unit

The carousel's content Composable