What You Will Build

A recipe browsing screen with icon-based category cards, 5-star ratings, and a scrollable list. Each card uses a rounded container icon, title, subtitle, and a row of star icons. This is the core list pattern for recipe, book, and media apps.

Why This Pattern Matters

List screens with rich cards are the most common screen type in content apps. This exercise covers LazyColumn with spacedBy arrangement, Box with clip and background for icon containers, and reusable Row layouts for metadata display.

Step 1: Define the Recipe Model

data class Recipe(
    val title: String,
    val subtitle: String,
    val icon: ImageVector
)

val recipes = listOf(
    Recipe("Pasta Carbonara", "Classic Italian comfort", Icons.Default.Restaurant),
    Recipe("Sushi Rolls", "Fresh Japanese cuisine", Icons.Default.SetMeal),
    Recipe("Caesar Salad", "Crispy and refreshing", Icons.Default.Spa),
    Recipe("Grilled Steak", "Perfectly seasoned", Icons.Default.LocalDining),
    Recipe("Banana Bread", "Sweet and moist", Icons.Default.Cake),
    Recipe("Smoothie Bowl", "Healthy breakfast option", Icons.Default.Blender)
)

Step 2: Build the Recipe Card

@Composable
fun RecipeCard(recipe: Recipe) {
    Card(Modifier.fillMaxWidth()) {
        Row(Modifier.padding(16.dp)) {
            Box(
                Modifier.size(80.dp)
                    .clip(RoundedCornerShape(12.dp))
                    .background(MaterialTheme.colorScheme.primaryContainer),
                contentAlignment = Alignment.Center
            ) {
                Icon(recipe.icon, null, modifier = Modifier.size(36.dp))
            }
            Spacer(Modifier.width(16.dp))
            Column(Modifier.weight(1f)) {
                Text(recipe.title, fontWeight = FontWeight.Bold, fontSize = 16.sp)
                Text(
                    recipe.subtitle,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    fontSize = 14.sp
                )
                Spacer(Modifier.height(8.dp))
                Row {
                    repeat(5) {
                        Icon(
                            Icons.Default.Star, null,
                            modifier = Modifier.size(16.dp),
                            tint = Color(0xFFFFD700)
                        )
                    }
                }
            }
        }
    }
}

Step 3: Assemble the List

Scaffold(topBar = { TopAppBar(title = { Text("Recipes") }) }) { padding ->
    LazyColumn(
        modifier = Modifier.padding(padding).padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        items(recipes) { recipe -> RecipeCard(recipe) }
    }
}

Tips and Pitfalls

  • MaterialTheme.colorScheme.primaryContainer automatically adapts the icon background to light/dark themes.
  • Arrangement.spacedBy(12.dp) is cleaner than adding Spacers between items manually.
  • Weight(1f) on the Column ensures the text section fills remaining space after the fixed-size icon box.
  • For real apps: Replace ImageVector icons with AsyncImage from Coil to load recipe photos from a backend.

Min SDK: 21 | Compose BOM: 2024.01.00+

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.