What You Will Build
A vertically scrolling list of story cards, each featuring an icon thumbnail, title, subtitle, and a star rating row. This pattern is used in book apps, podcast directories, course catalogs, and any content library with ratings.
Why This Pattern Matters
Card-based lists with thumbnails and metadata are the most common UI pattern in Android apps. Getting the Row-inside-Card layout right with proper spacing, alignment, and weight distribution is foundational for building any content browsing experience.
Key Concepts
- LazyColumn with Arrangement.spacedBy for consistent card spacing.
- Row inside Card with thumbnail, text column, and weighted layout.
- repeat() for star ratings generating identical icons efficiently.
The Stories Screen
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StoriesScreen() {
data class Item(
val title: String,
val subtitle: String,
val icon: ImageVector
)
val items = listOf(
Item("The Art of Code", "A journey through software", Icons.Default.Book),
Item("Design Patterns", "Building better apps", Icons.Default.DesignServices),
Item("Mobile First", "Modern development", Icons.Default.PhoneAndroid),
Item("AI Revolution", "The future is here", Icons.Default.SmartToy),
Item("Clean Architecture", "Principles and practices", Icons.Default.Architecture),
Item("Data Stories", "Visualizing insights", Icons.Default.BarChart)
)
Scaffold(
topBar = { TopAppBar(title = { Text("Stories") }) }
) { padding ->
LazyColumn(
modifier = Modifier.padding(padding).padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(items) { item ->
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(item.icon, null,
modifier = Modifier.size(36.dp))
}
Spacer(Modifier.width(16.dp))
Column(Modifier.weight(1f)) {
Text(item.title,
fontWeight = FontWeight.Bold,
fontSize = 16.sp)
Text(item.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))
}
}
}
}
}
}
}
}
}
Tips and Pitfalls
- Modifier.weight(1f) on the text Column ensures it fills the remaining space after the fixed-size thumbnail.
- RoundedCornerShape(12.dp) on the thumbnail box creates rounded thumbnails without needing images.
- Star ratings: Use
repeat(5)with conditional tint colors for partial ratings (filled vs outlined stars). - Click handling: Add
Modifier.clickable { }on the Card for navigation to a detail screen.
Minimum SDK: API 21+ with Compose BOM