Product Details

Use these Compose Multiplatform Product Details components to educate your users about all details of your products.Simply copy and paste them into your projects

Product Details

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.composables.uripainter.rememberUriPainter
import kotlinx.coroutines.launch

@Composable
fun ProductDetails() {
    data class ProductColor(val label: String, val color: Long)
    data class ProductSizes(val label: String, val available: Boolean = true)

    val photos = listOf(
        "https://images.unsplash.com/photo-1545066230-919660a9290a?q=80&w=1024&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1464278860589-b2ed64f87e22?q=80&w=1024&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1455095692583-a6db7b07d3f3?q=80&w=1024&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1509316557442-08a701c1ecf1?q=80&w=1024&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1495772507711-3afc975ff36e?q=80&w=1024&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
    )

    val productColors = listOf(
        ProductColor(label = "Brown", color = 0xFF795548),
        ProductColor(label = "Deep Red", color = 0xFFD32F2F),
        ProductColor(label = "White", color = 0xFFFFFF),
        ProductColor(label = "Black", color = 0xFF000000),
    )
    val productSizes = listOf(
        ProductSizes(label = "XXS", available = false),
        ProductSizes(label = "XS"),
        ProductSizes(label = "S"),
        ProductSizes(label = "M"),
        ProductSizes(label = "L"),
        ProductSizes(label = "XL"),
        ProductSizes(label = "XXL", available = false),
        ProductSizes(label = "XXXL", available = false),
    )

    BoxWithConstraints(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
        @Composable
        fun HeroSection(animateCornerRadius: Boolean, modifier: Modifier = Modifier) {
            Box(modifier) {
                val pagerState = rememberPagerState(initialPage = 0, pageCount = { photos.size })
                val previousEnabled = pagerState.currentPage > 0
                val nextEnabled = pagerState.currentPage < photos.lastIndex
                val scope = rememberCoroutineScope()

                val cornerRadius by animateDpAsState(targetValue = if (pagerState.isScrollInProgress || animateCornerRadius.not()) 8.dp else 0.dp)

                HorizontalPager(
                    modifier = Modifier.matchParentSize(),
                    state = pagerState,
                    pageSpacing = 8.dp,
                    beyondViewportPageCount = 2,
                ) { i ->
                    Image(
                        painter = rememberUriPainter(photos[i]),
                        contentDescription = null,
                        modifier = Modifier.clip(RoundedCornerShape(cornerRadius)).fillMaxSize(),
                        contentScale = ContentScale.Crop
                    )
                }
                AnimatedVisibility(
                    visible = previousEnabled,
                    enter = slideInHorizontally { -it } + fadeIn(),
                    exit = slideOutHorizontally { -it * 2 } + fadeOut(),
                    modifier = Modifier.align(Alignment.CenterStart).padding(start = 16.dp)) {
                    Card(onClick = { scope.launch { pagerState.animateScrollToPage(pagerState.currentPage - 1) } }) {
                        Icon(
                            Icons.Filled.ChevronLeft,
                            contentDescription = "See previous photo",
                            modifier = Modifier.padding(8.dp)
                        )
                    }
                }
                AnimatedVisibility(
                    visible = nextEnabled,
                    enter = slideInHorizontally { it } + fadeIn(),
                    exit = slideOutHorizontally { it * 2 } + fadeOut(),
                    modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp)
                ) {
                    Card(onClick = { scope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } }) {
                        Icon(
                            Icons.Filled.ChevronRight,
                            contentDescription = "See next photo",
                            modifier = Modifier.padding(8.dp)
                        )
                    }
                }
            }
        }

        val useHorizontalLayout = maxWidth > 600.dp
        Row(Modifier.widthIn(max = 1200.dp).fillMaxWidth()) {
            if (useHorizontalLayout) {
                HeroSection(animateCornerRadius = false, Modifier.padding(16.dp).height(800.dp).fillMaxWidth(1 / 2f))
            }
            LazyColumn(Modifier.fillMaxHeight()) {
                if (useHorizontalLayout.not()) {
                    item {
                        HeroSection(animateCornerRadius = true, Modifier.aspectRatio(4 / 5f).fillMaxWidth())
                    }
                }
                item {
                    Spacer(Modifier.height(16.dp).fillMaxWidth())
                }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Off-road boots", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(4.dp))
                        Text("$340")
                    }
                }
                item {
                    Row(
                        modifier = Modifier
                            .padding(horizontal = 16.dp)
                            .offset(x = -4.dp).clip(RoundedCornerShape(4.dp))
                            .clickable { /* TODO */ }
                            .padding(vertical = 4.dp),
                        verticalAlignment = Alignment.Bottom) {
                        Row(horizontalArrangement = Arrangement.spacedBy(-2.dp)) {
                            repeat(4) {
                                Icon(Icons.Rounded.Star, contentDescription = null, tint = Color(0xFFFFC107))
                            }
                            Icon(
                                Icons.Rounded.Star,
                                contentDescription = null,
                                tint = MaterialTheme.colorScheme.outline
                            )
                        }
                        Spacer(Modifier.width(4.dp))
                        Text(
                            text = "4.8 (3.4)",
                            color = MaterialTheme.colorScheme.primary,
                            fontWeight = FontWeight.Medium
                        )
                    }
                }
                item { Spacer(Modifier.height(16.dp).fillMaxWidth()) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Color")
                        Spacer(Modifier.height(4.dp))
                        Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                            productColors.forEach { availableColor ->
                                Box(
                                    modifier = Modifier.size(32.dp).clip(CircleShape).clickable(
                                        role = Role.Button, onClickLabel = "Select ${availableColor.label} color"
                                    ) { /* TODO */ }.border(1.dp, MaterialTheme.colorScheme.outline, CircleShape)
                                        .padding(1.dp)
                                        .background(Color(availableColor.color), CircleShape)
                                )
                            }
                        }
                    }
                }
                item {
                    Spacer(Modifier.height(16.dp).fillMaxWidth())
                }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
                            Text(
                                modifier = Modifier.alignByBaseline(),
                                text = "Size"
                            )
                            Text(
                                modifier = Modifier.alignByBaseline().offset(x = 4.dp).clip(RoundedCornerShape(4.dp))
                                    .clickable { /* TODO */ }.padding(4.dp),
                                text = "Size Chart",
                                color = MaterialTheme.colorScheme.primary,
                                fontWeight = FontWeight.Medium
                            )
                        }
                        Spacer(Modifier.height(4.dp))
                        FlowRow(
                            maxItemsInEachRow = 4,
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(2.dp),
                            verticalArrangement = Arrangement.spacedBy(2.dp)
                        ) {
                            productSizes.forEach { sizes ->
                                Surface(
                                    onClick = { /* TODO */ },
                                    enabled = sizes.available,
                                    modifier = Modifier.weight(1f),
                                    shape = RoundedCornerShape(4.dp),
                                    color = if (sizes.available) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceVariant,
                                    border = androidx.compose.foundation.BorderStroke(
                                        1.dp,
                                        MaterialTheme.colorScheme.outline
                                    )
                                ) {
                                    Box(
                                        modifier = Modifier.padding(vertical = 12.dp).fillMaxWidth(),
                                        contentAlignment = Alignment.Center
                                    ) {
                                        Text(sizes.label)
                                    }
                                }
                            }
                        }
                    }
                }
                item {
                    Spacer(Modifier.height(16.dp).fillMaxWidth())
                }
                item {
                    Button(
                        onClick = { /* TODO */ },
                        modifier = Modifier.padding(horizontal = 16.dp).fillMaxWidth()
                    ) {
                        Text("Add to Cart")
                    }
                }
                item { Spacer(Modifier.height(32.dp).fillMaxWidth()) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Product Details", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(8.dp))
                        Text("• Durable leather construction")
                        Text("• Lace-up fastening")
                        Text("• Padded collar for comfort")
                        Text("• Rugged sole for traction")
                    }
                }
                item { Spacer(Modifier.height(32.dp).fillMaxWidth()) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("About this item", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(8.dp))
                        Text("• Perfect for off-road adventures")
                        Text("• Sturdy and long-lasting")
                        Text("• Designed for optimal foot support")
                        Text("• Stylish and practical")
                    }
                }
                item { Spacer(Modifier.height(32.dp).fillMaxWidth()) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Size & Fit", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(8.dp))
                        Text("• Fits true to size")
                        Text("• Comfortable fit with padded collar")
                        Text("• Great ankle support")
                        Text("• Suitable for wide feet")
                    }
                }

                item { Spacer(Modifier.height(32.dp).fillMaxWidth()) }
            }
        }
    }
}

Be notified when we ship more UI Blocks

Subscribe to get updates on when we ship new components.

Product Details With Image Grid

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.composables.uripainter.rememberUriPainter
import kotlinx.coroutines.launch

@Composable
fun ProductDetailsWithImageGrid() {
    data class ProductColor(val label: String, val color: Long)
    data class ProductSizes(val label: String, val available: Boolean = true)

    val photos = listOf(
        "https://images.unsplash.com/photo-1545066230-919660a9290a?q=80&w=700&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1464278860589-b2ed64f87e22?q=80&w=700&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1455095692583-a6db7b07d3f3?q=80&w=700&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1509316557442-08a701c1ecf1?q=80&w=700&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
        "https://images.unsplash.com/photo-1495772507711-3afc975ff36e?q=80&w=7006&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
    )

    val productColors = listOf(
        ProductColor(label = "Brown", color = 0xFF795548),
        ProductColor(label = "Deep Red", color = 0xFFD32F2F),
        ProductColor(label = "White", color = 0xFFFFFF),
        ProductColor(label = "Black", color = 0xFF000000),
    )
    val productSizes = listOf(
        ProductSizes(label = "XXS", available = false),
        ProductSizes(label = "XS"),
        ProductSizes(label = "S"),
        ProductSizes(label = "M"),
        ProductSizes(label = "L"),
        ProductSizes(label = "XL"),
        ProductSizes(label = "XXL", available = false),
        ProductSizes(label = "XXXL", available = false),
    )

    BoxWithConstraints(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
        @Composable
        fun ImageCollage(photos: List<String>, modifier: Modifier) {
            Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                Image(
                    rememberUriPainter(photos[0]),
                    modifier = Modifier.clip(RoundedCornerShape(4.dp)).weight(1f).fillMaxHeight(),
                    contentDescription = null,
                    contentScale = ContentScale.Crop
                )
                Row(Modifier.weight(1f).fillMaxHeight(), horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                    Column(Modifier.weight(1f).fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(4.dp)) {
                        listOf(photos[1], photos[2]).forEach { photo ->
                            Image(
                                rememberUriPainter(photo),
                                modifier = Modifier.clip(RoundedCornerShape(4.dp)).weight(1f).fillMaxHeight(),
                                contentDescription = null,
                                contentScale = ContentScale.Crop
                            )
                        }
                    }
                    Column(Modifier.weight(1f).fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(4.dp)) {
                        listOf(photos[3], photos[4]).forEach { photo ->
                            Image(
                                rememberUriPainter(photo),
                                modifier = Modifier.clip(RoundedCornerShape(4.dp)).weight(1f).fillMaxHeight(),
                                contentDescription = null,
                                contentScale = ContentScale.Crop
                            )
                        }
                    }
                }
            }
        }

        @Composable
        fun HeroSection(animateCornerRadius: Boolean, modifier: Modifier = Modifier) {
            Box(modifier) {
                val pagerState = rememberPagerState(initialPage = 0, pageCount = { photos.size })
                val previousEnabled = pagerState.currentPage > 0
                val nextEnabled = pagerState.currentPage < photos.lastIndex
                val scope = rememberCoroutineScope()

                val cornerRadius by animateDpAsState(targetValue = if (pagerState.isScrollInProgress || animateCornerRadius.not()) 8.dp else 0.dp)

                HorizontalPager(
                    modifier = Modifier.matchParentSize(),
                    state = pagerState,
                    pageSpacing = 8.dp,
                    beyondViewportPageCount = 2,
                ) { i ->
                    Image(
                        rememberUriPainter(photos[i]),
                        contentDescription = null,
                        modifier = Modifier.clip(RoundedCornerShape(cornerRadius)).fillMaxSize(),
                        contentScale = ContentScale.Crop
                    )
                }
                AnimatedVisibility(
                    visible = previousEnabled,
                    enter = slideInHorizontally { -it } + fadeIn(),
                    exit = slideOutHorizontally { -it * 2 } + fadeOut(),
                    modifier = Modifier.align(Alignment.CenterStart).padding(start = 16.dp)) {
                    Card(onClick = { scope.launch { pagerState.animateScrollToPage(pagerState.currentPage - 1) } }) {
                        Icon(
                            Icons.Filled.ChevronLeft,
                            contentDescription = "See previous photo",
                            modifier = Modifier.padding(8.dp)
                        )
                    }
                }
                AnimatedVisibility(
                    visible = nextEnabled,
                    enter = slideInHorizontally { it } + fadeIn(),
                    exit = slideOutHorizontally { it * 2 } + fadeOut(),
                    modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp)) {
                    Card(onClick = { scope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } }) {
                        Icon(
                            Icons.Filled.ChevronRight,
                            contentDescription = "See next photo",
                            modifier = Modifier.padding(8.dp)
                        )
                    }
                }
            }
        }

        val useSingleColumn = maxWidth < 600.dp

        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
            LazyColumn(Modifier.widthIn(max = 1200.dp)) {
                if (useSingleColumn) {
                    item {
                        HeroSection(animateCornerRadius = true, Modifier.aspectRatio(4 / 5f).fillMaxWidth())
                    }
                } else {
                    item {
                        ImageCollage(
                            photos = photos,
                            modifier = Modifier.fillMaxWidth().aspectRatio(16 / 9f).padding(horizontal = 16.dp)
                        )
                    }
                }
                item {
                    Spacer(Modifier.height(16.dp).fillMaxWidth())
                }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Off-road boots", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(4.dp))
                        Text("$340")
                    }
                }
                item {
                    Row(
                        modifier = Modifier.padding(horizontal = 16.dp).offset(x = (-4).dp)
                            .clip(RoundedCornerShape(4.dp)).clickable { /* TODO */ }.padding(vertical = 4.dp),
                        verticalAlignment = Alignment.CenterVertically,
                    ) {
                        Row(horizontalArrangement = Arrangement.spacedBy(-2.dp)) {
                            repeat(4) {
                                Icon(Icons.Rounded.Star, contentDescription = null, tint = Color(0xFFFFC107))
                            }
                            Icon(
                                Icons.Rounded.Star,
                                contentDescription = null,
                                tint = MaterialTheme.colorScheme.outline
                            )
                        }
                        Spacer(Modifier.width(4.dp))
                        Text("4.8 (3.4)", color = MaterialTheme.colorScheme.primary, fontWeight = FontWeight.Medium)
                    }
                }
                item { Spacer(Modifier.height(16.dp).fillMaxWidth()) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Color")
                        Spacer(Modifier.height(4.dp))
                        Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                            productColors.forEach { availableColor ->
                                Box(
                                    modifier = Modifier
                                        .size(32.dp)
                                        .clip(CircleShape)
                                        .clickable(
                                            role = Role.Button,
                                            onClickLabel = "Select ${availableColor.label} color"
                                        ) { /* TODO */ }
                                        .border(1.dp, MaterialTheme.colorScheme.outline, CircleShape)
                                        .padding(1.dp)
                                        .background(Color(availableColor.color), CircleShape)
                                )
                            }
                        }
                    }
                }
                item {
                    Spacer(Modifier.height(16.dp).fillMaxWidth())
                }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
                            Text(
                                modifier = Modifier.alignByBaseline(),
                                text = "Size",
                            )
                            Text(
                                modifier = Modifier
                                    .alignByBaseline()
                                    .offset(x = 4.dp)
                                    .clip(RoundedCornerShape(4.dp))
                                    .clickable { /* TODO */ }.padding(4.dp),
                                text = "Size Chart"
                            )
                        }
                        Spacer(Modifier.height(4.dp))
                        FlowRow(
                            maxItemsInEachRow = if (useSingleColumn) 4 else Int.MAX_VALUE,
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(2.dp),
                            verticalArrangement = Arrangement.spacedBy(2.dp)
                        ) {
                            productSizes.forEach { sizes ->
                                Surface(
                                    onClick = { /* TODO */ },
                                    enabled = sizes.available,
                                    modifier = Modifier.weight(1f),
                                    shape = RoundedCornerShape(4.dp),
                                    color = if (sizes.available) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceVariant,
                                    border = androidx.compose.foundation.BorderStroke(
                                        1.dp,
                                        MaterialTheme.colorScheme.outline
                                    )
                                ) {
                                    Box(
                                        modifier = Modifier.padding(vertical = 12.dp).fillMaxWidth(),
                                        contentAlignment = Alignment.Center
                                    ) {
                                        Text(sizes.label)
                                    }
                                }
                            }
                        }
                    }
                }
                item { HorizontalDivider(Modifier.padding(16.dp)) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Product Details", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(8.dp))
                        Text("• Durable leather construction")
                        Text("• Lace-up fastening")
                        Text("• Padded collar for comfort")
                        Text("• Rugged sole for traction")
                    }
                }
                item { HorizontalDivider(Modifier.padding(16.dp)) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("About this item", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(8.dp))
                        Text("• Perfect for off-road adventures")
                        Text("• Sturdy and long-lasting")
                        Text("• Designed for optimal foot support")
                        Text("• Stylish and practical")
                    }
                }
                item { HorizontalDivider(Modifier.padding(16.dp)) }
                item {
                    Column(Modifier.padding(horizontal = 16.dp)) {
                        Text("Size & Fit", style = MaterialTheme.typography.titleLarge)
                        Spacer(Modifier.height(8.dp))
                        Text("• Fits true to size")
                        Text("• Comfortable fit with padded collar")
                        Text("• Great ankle support")
                        Text("• Suitable for wide feet")
                    }
                }
                item {
                    Spacer(Modifier.height(84.dp).fillMaxWidth())
                }
            }

            Box(
                modifier = Modifier.shadow(4.dp).background(MaterialTheme.colorScheme.surface).fillMaxWidth()
                    .align(Alignment.BottomCenter),
                contentAlignment = Alignment.Center
            ) {
                Row(
                    horizontalArrangement = Arrangement.SpaceBetween,
                    modifier = Modifier.widthIn(max = 1200.dp).fillMaxWidth().align(Alignment.BottomCenter)
                        .padding(vertical = 12.dp)
                ) {
                    Column(Modifier.padding(horizontal = 16.dp).weight(1f)) {
                        Text("Off-road boots")
                        Spacer(Modifier.height(4.dp))
                        Text("$340")
                    }

                    Button(onClick = { /* TODO */ }) {
                        Text("Add to Cart")
                    }
                }
            }
        }
    }
}

Be notified when we ship more UI Blocks

Subscribe to get updates on when we ship new components.

Explore other Jetpack Compose Blocks