
Layouts are the building blocks of any UI using Layouts in Jetpack Compose. In the old View system we had LinearLayout, RelativeLayout, ConstraintLayout. In Compose we have Column, Row, and Box as the core primitives β and they solve 90% of everyday layout needs. This chapter explains them with simple examples and real-world patterns (list item, avatar + text row, overlapping images), plus tips youβll actually use.
πΉ The mental model (quick)
Column= vertical stacking (one below another). Like a vertical LinearLayout.Row= horizontal stacking (one next to another). Like a horizontal LinearLayout.Box= z-stack / frame β children overlap; useful for badges, overlays, stacking.LazyColumn/LazyRow= recyclable list (like RecyclerView). Use these for long lists.
Two important concepts:
- Arrangement controls how extra space is distributed among children along the main axis.
- Alignment controls how children are positioned on the cross axis.
Remember this:
Columnβs main axis = vertical; cross axis = horizontal.
Rowβs main axis = horizontal; cross axis = vertical.
β Example 1 β Column (vertical list) with Arrangement & Alignment
@Preview(showBackground = true)
@Composable
fun ColumnExample() {
Column(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.padding(16.dp),
verticalArrangement = Arrangement.SpaceEvenly, // distributes vertical space evenly
horizontalAlignment = Alignment.CenterHorizontally // horizontal center
) {
Text("Item A", fontSize = 20.sp)
Text("Item B", fontSize = 18.sp)
Text("Item C", fontSize = 16.sp)
}
}
Key points:
Arrangement.SpaceEvenlyplaces equal space before/after/between children.horizontalAlignment = Alignment.Start/CenterHorizontally/Endcontrols cross-axis alignment.
Other useful Arrangements:
SpaceBetweenβ max space between items, none at ends.Top,Bottom,Center(orTop,CenterVerticallyetc depending on context).
β Example 2 β Row (horizontal) with weight and spacing
Common scenario: icon + text + trailing timestamp. Use weight for flexible widths.
@Composable
fun RowItemExample() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Image(
painter = painterResource(R.drawable.ic_avatar),
contentDescription = "avatar",
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text("Title", fontWeight = FontWeight.Bold)
Text("Subtitle or small description", fontSize = 12.sp, color = Color.Gray)
}
Text("2h", fontSize = 12.sp, color = Color.Gray) // trailing
}
}
Tips:
- Put the flexible content in
.weight(1f)so trailing elements stay snug to the end. Spaceris your friend for fixed gaps.
β Example 3 β Box: overlays, badges, backgrounds
Box stacks children; first child drawn below, next on top.
@Preview(showBackground = true)
@Composable
fun BoxBadgeExample() {
Box(modifier = Modifier.size(120.dp)) {
Image(
painter = painterResource(R.drawable.sample_cover),
contentDescription = "cover",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
// A small badge at top-right
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(8.dp)
.background(Color.Black.copy(alpha = 0.6f), shape = CircleShape)
.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Text("NEW", color = Color.White, fontSize = 12.sp)
}
}
}
Use align(Alignment.X) on child to place it inside the Box.
β Example 4 β Creating a List Item (image + texts) and rendering it many times
Hereβs a typical list item: avatar/image left, column of texts right. Then render with a LazyColumn.
data class Device(val imageRes: Int, val name: String, val info: String)
@Composable
fun DeviceListItem(device: Device) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(device.imageRes),
contentDescription = null,
modifier = Modifier.size(56.dp).clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(device.name, fontWeight = FontWeight.Bold)
Text(device.info, fontSize = 12.sp, color = Color.Gray)
}
IconButton(onClick = { /* action */ }) {
Icon(Icons.Default.MoreVert, contentDescription = "menu")
}
}
}
@Preview(showBackground = true)
@Composable
fun DeviceListPreview() {
val items = List(10) { i ->
Device(R.drawable.sample_cover, "Device $i", "${8 + i} GB RAM")
}
LazyColumn {
items(items) { device ->
DeviceListItem(device)
Divider()
}
}
}
Important:
LazyColumnis the Compose equivalent of RecyclerView β it lazily composes items, efficient for long lists.- Use
items()oritemsIndexed()for lists.
β Example 5 β Horizontal list (LazyRow)
@Preview(showBackground = true)
@Composable
fun HorizontalListPreview() {
val images = List(8) { R.drawable.sample_cover }
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(12.dp)) {
items(images) { img ->
Image(
painter = painterResource(img),
contentDescription = null,
modifier = Modifier.size(140.dp).clip(RoundedCornerShape(10.dp)),
contentScale = ContentScale.Crop
)
}
}
}
π§ Modifier basics youβll use every day
Modifier is the glue. Common ones:
.fillMaxWidth(),.fillMaxSize().wrapContentHeight(),.height(200.dp),.width(48.dp).padding(12.dp),.offset(x=..., y=...).size(40.dp)β convenience for both width+height.clip(RoundedCornerShape(...))β for rounded corners.background(Color, shape)β gives background color and shape.weight(1f)β used inside Row/Column to take proportional space.clickable { }β for clickable behavior
Example: Modifier.weight(1f).padding(8.dp) β flexible plus spacing.
π§ Common gotchas & developer tricks
- Use weight for flexible content
When aRowhas a trailing fixed element (button, icon), use.weight(1f)on the center column to push trailing content to the end. - Prefer Lazy lists for long content
Columnwith.verticalScroll()composes everything β bad for large lists. UseLazyColumnso items are recycled. - Use
ArrangementvsAlignmentintentionally
Arrangement= how leftover main-axis space is distributed.Alignment= cross-axis positioning. Confusing them causes odd layout results. - Modifier order matters
Modifier.padding().clickable()behaves differently thanModifier.clickable().padding()in some edge cases (padding influences clickable area). Usually put clickable at end when you want padding included in touch target. - Box for overlays
Want badge, gradient overlay, or text over image? UseBoxandalign(...)for children. - Performance
Keep composables small and stateless where possible. If a composable does heavy work, move that to a ViewModel or remember-derived-state to avoid expensive recompositions. - Preview multiple sizes
Use multiple@Previewannotations to test different screen sizes and dark/light themes.
π¦ Example: Combining everything β a small catalog card list
@Composable
fun CatalogCard(item: Device) {
Card(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Row(modifier = Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically) {
Image(painter = painterResource(item.imageRes),
contentDescription = null,
modifier = Modifier.size(64.dp).clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Crop)
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(item.name, fontWeight = FontWeight.Bold)
Text(item.info, fontSize = 12.sp, color = Color.Gray)
}
Button(onClick = { /* buy */ }) {
Text("Buy")
}
}
}
}
@Composable
fun CatalogList(items: List<Device>) {
LazyColumn {
items(items) { CatalogCard(it) }
}
}
This pattern is what youβll use for feeds, product lists, message lists β everything.
π§© Final checklist before you ship a layout
- Does the layout adapt to screen size and orientation? Test different devices.
- Are touch targets at least 48dp? Buttons/icons should be tappable.
- Does the list use
LazyColumnfor many items? If not, switch. - Do you use
rememberfor expensive computations used in composables? - Have you added content descriptions for images for accessibility? (
contentDescription)
π Whatβs next (Chapter 5 preview)
In the next chapter weβll deep-dive into Modifiers & Custom Layouts: how to create your own layout composable, write performant modifiers, and build a responsive grid. Youβll also learn when to use Layout/SubcomposeLayout for advanced scenarios.
Made with β€οΈ by codewithpk.com
Keep building. Keep learning. Keep shipping.
