Jetpack Compose — Your First Screen (Text, Button, Column, Row)
Build your first Jetpack Compose screen from scratch — Text, Button, Column, Row, modifiers, and previews. Everything you need to start building UI in Compose.
This is Part 1 of a 10-part series on Jetpack Compose.
- Your First Screen (this post)
- State Management
- Navigation
- Lists
- Theming
- Forms
- Side Effects
- Custom Layouts
- API Integration
- Migration from XML
What is Jetpack Compose?
Jetpack Compose is Android’s modern toolkit for building UI. Instead of XML layouts and findViewById, you write UI as Kotlin functions. The framework handles rendering and updates.
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
That’s a composable function. It takes data, returns UI. No XML, no view binding, no inflation. Compose has been stable since July 2021 and is now the recommended way to build Android UI.
Setup
Add Compose dependencies to your build.gradle.kts:
android {
buildFeatures {
compose = true
}
}
dependencies {
val composeBom = platform("androidx.compose:compose-bom:2026.02.01")
implementation(composeBom)
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.10.1")
debugImplementation("androidx.compose.ui:ui-tooling")
}
The BOM (Bill of Materials) manages version alignment — you specify the BOM version and all Compose libraries use compatible versions.
Your first composable
In your Activity:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface {
Greeting("Compose")
}
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
setContent replaces setContentView. Inside it, you call composable functions. MaterialTheme applies Material Design 3 styling. Surface provides a background.
Text
import androidx.compose.material3.Text
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
@Composable
fun TextExamples() {
// Basic text
Text(text = "Hello, World!")
// Styled text
Text(
text = "Bold and large",
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
// Material typography
Text(
text = "Headline",
style = MaterialTheme.typography.headlineMedium
)
// Text with color
Text(
text = "Colored text",
color = MaterialTheme.colorScheme.primary
)
}
Use MaterialTheme.typography for consistent text styles across the app. Avoid hardcoding sizes — let the theme handle it.
Button
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.TextButton
import androidx.compose.material3.Text
@Composable
fun ButtonExamples() {
// Filled button (primary action)
Button(onClick = { /* handle click */ }) {
Text("Save")
}
// Outlined button (secondary action)
OutlinedButton(onClick = { /* handle click */ }) {
Text("Cancel")
}
// Text button (tertiary action)
TextButton(onClick = { /* handle click */ }) {
Text("Skip")
}
}
Buttons take an onClick lambda and a content lambda. The content is typically Text, but can be any composable (icons, rows, etc.).
Column — Vertical layout
Column stacks children vertically. It’s the Compose equivalent of a vertical LinearLayout.
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun ProfileCard() {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Alice Johnson",
style = MaterialTheme.typography.headlineSmall
)
Text(
text = "[email protected]",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "Software Developer",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Alignment and arrangement
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Alignment
@Composable
fun CenteredContent() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Centered vertically and horizontally")
}
}
verticalArrangementcontrols spacing between children:Top,Center,Bottom,SpaceBetween,SpaceEvenly,SpaceAroundhorizontalAlignmentaligns children horizontally:Start,CenterHorizontally,End
Row — Horizontal layout
Row places children side by side. Equivalent of a horizontal LinearLayout.
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Star
@Composable
fun RatingRow(rating: Float) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Default.Star,
contentDescription = "Rating",
tint = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "$rating / 5.0",
style = MaterialTheme.typography.bodyMedium
)
}
}
Spacer adds empty space. Modifier.width() sets its size.
Modifiers — The styling system
Modifiers are how you style, position, and add behavior to composables. They chain together:
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.clip
@Composable
fun StyledBox() {
Text(
text = "Click me",
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.background(MaterialTheme.colorScheme.primaryContainer)
.clickable { /* handle click */ }
.padding(16.dp)
)
}
Order matters. Modifiers apply from outer to inner:
// Padding outside the background (margin-like)
Modifier
.padding(16.dp)
.background(Color.Red)
// Padding inside the background
Modifier
.background(Color.Red)
.padding(16.dp)
Common modifiers
| Modifier | Purpose |
|---|---|
padding(dp) | Inner spacing |
fillMaxWidth() | Take full width |
fillMaxSize() | Take full width and height |
size(dp) | Fixed size |
weight(float) | Proportional size in Row/Column |
background(color) | Background color |
clip(shape) | Clip to shape |
clickable { } | Handle clicks |
border(width, color) | Border |
Box — Layered layout
Box stacks children on top of each other. Like a FrameLayout:
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.ui.Alignment
@Composable
fun LoadingOverlay(isLoading: Boolean) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text("Content here")
if (isLoading) {
CircularProgressIndicator()
}
}
}
Use Box for overlapping content — loading indicators over content, badges on icons, floating action buttons.
Building a real screen
Let’s combine everything into a simple profile screen:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
@Composable
fun ProfileScreen(
name: String,
email: String,
bio: String,
postCount: Int,
followerCount: Int,
onEditClick: () -> Unit,
onLogoutClick: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// Avatar placeholder
Box(
modifier = Modifier
.size(80.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primaryContainer),
contentAlignment = Alignment.Center
) {
Text(
text = name.first().toString(),
style = MaterialTheme.typography.headlineLarge,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = name,
style = MaterialTheme.typography.headlineSmall
)
Text(
text = email,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = bio,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(24.dp))
// Stats row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
StatItem(label = "Posts", count = postCount)
StatItem(label = "Followers", count = followerCount)
}
Spacer(modifier = Modifier.height(24.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(24.dp))
// Action buttons
Button(
onClick = onEditClick,
modifier = Modifier.fillMaxWidth()
) {
Text("Edit Profile")
}
Spacer(modifier = Modifier.height(8.dp))
OutlinedButton(
onClick = onLogoutClick,
modifier = Modifier.fillMaxWidth()
) {
Text("Log Out")
}
}
}
@Composable
fun StatItem(label: String, count: Int) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = count.toString(),
style = MaterialTheme.typography.titleLarge
)
Text(
text = label,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Every composable takes its data as parameters. No findViewById, no view binding, no XML inflation. The UI is a function of its inputs.
Previews
Preview composables in Android Studio without running the app:
import androidx.compose.ui.tooling.preview.Preview
@Preview(showBackground = true)
@Composable
fun ProfileScreenPreview() {
MaterialTheme {
ProfileScreen(
name = "Alice Johnson",
email = "[email protected]",
bio = "Android developer and coffee enthusiast",
postCount = 42,
followerCount = 1200,
onEditClick = {},
onLogoutClick = {}
)
}
}
@Preview renders the composable in the IDE’s preview pane. Add showBackground = true for a white background. You can create multiple previews — light mode, dark mode, different screen sizes.
Key concepts to remember
- Composables are functions — they take parameters and describe UI
- Modifiers are how you style — chain them, order matters
- Column = vertical, Row = horizontal, Box = layered
- Use Material theme —
MaterialTheme.typography,MaterialTheme.colorScheme - Preview everything —
@Previewlets you iterate fast
Next up: State Management in Compose — how to make your UI interactive with remember, mutableStateOf, and ViewModel.