What You Will Build

A social media feed screen with a notification bell in the top bar, user posts featuring circular avatar initials, content text, like counts, comment buttons, and share icons. Each post is separated by a Material 3 HorizontalDivider for clean visual separation.

Why This Pattern Matters

Every social app (Twitter/X, Instagram, LinkedIn) is built on a feed pattern. This project covers the essential building blocks: Scaffold with action icons, LazyColumn for efficient scrolling, avatar composables, and interaction row layouts.

Step 1: Scaffold with Notifications

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SocialAppScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Social Feed") },
                actions = {
                    IconButton(onClick = {}) {
                        Icon(Icons.Default.Notifications,
                             "Notifications")
                    }
                }
            )
        }
    ) { padding ->
        // Feed content
    }
}

Step 2: Feed Post Layout

Column(Modifier.padding(16.dp)) {
    // User header
    Row(verticalAlignment = Alignment.CenterVertically) {
        Box(
            Modifier.size(40.dp).clip(CircleShape)
                .background(MaterialTheme.colorScheme
                    .primaryContainer),
            contentAlignment = Alignment.Center
        ) {
            Text(post.user.first().toString(),
                 fontWeight = FontWeight.Bold)
        }
        Spacer(Modifier.width(12.dp))
        Column {
            Text(post.user, fontWeight = FontWeight.Bold)
            Text("2h ago", fontSize = 12.sp,
                 color = MaterialTheme.colorScheme
                     .onSurfaceVariant)
        }
    }
    Spacer(Modifier.height(8.dp))
    // Post content
    Text(post.content)
    Spacer(Modifier.height(8.dp))
    // Interactions
    Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
        Row(verticalAlignment = Alignment.CenterVertically) {
            Icon(Icons.Default.FavoriteBorder, "Like",
                 modifier = Modifier.size(20.dp))
            Spacer(Modifier.width(4.dp))
            Text("${post.likes}")
        }
        Row(verticalAlignment = Alignment.CenterVertically) {
            Icon(Icons.Default.ChatBubbleOutline, "Comment",
                 modifier = Modifier.size(20.dp))
            Spacer(Modifier.width(4.dp))
            Text("Reply")
        }
        Icon(Icons.Default.Share, "Share",
             modifier = Modifier.size(20.dp))
    }
    HorizontalDivider(Modifier.padding(top = 12.dp))
}

Tips and Pitfalls

  • LazyColumn vs Column: Always use LazyColumn for feeds that could grow. Column renders all items at once, causing memory issues with large datasets.
  • Key parameter in items(): Add key = { post.id } for stable identity during list updates, which enables item animations.
  • Icon sizing at 20.dp keeps interaction buttons small and unobtrusive while remaining tappable.
  • Real app extension: Add pull-to-refresh with pullToRefresh modifier (Material 3) or the Accompanist library.

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.