Jetpack Compose
Build modern Android UIs declaratively with Kotlin — composables, state, and Material components
@Composable
State
Column / Row
Material3
1Introduction to Jetpack Compose
Jetpack Compose is Android's modern UI toolkit for building native interfaces using Kotlin. Instead of XML layouts, you describe UI with @Composable functions. When state changes, Compose automatically updates only the parts of the UI that changed — called recomposition.
Imperative (XML + Views) Declarative (Compose)
───────────────────────── ─────────────────────
findViewById, setText State changes → UI recomposes
Manual visibility toggles @Composable functions describe UI
activity_main.xml setContent { MyScreen() }
XML Views vs Compose
| XML / Views | Jetpack Compose |
TextView | Text() |
Button | Button { Text("Click") } |
LinearLayout | Column / Row |
RecyclerView | LazyColumn |
View Binding | Not needed — UI is Kotlin code |
Enable Compose in Gradle
app/build.gradle.ktsandroid {
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.8"
}
}
dependencies {
val composeBom = platform("androidx.compose:compose-bom:2024.02.00")
implementation(composeBom)
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.8.2")
}
First Compose Activity
MainActivity.ktclass MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting(name = "Kotlin")
}
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(
text = "Hello, $name!",
modifier = Modifier.padding(16.dp)
)
}
Prerequisites
Know Kotlin basics, null safety, and lambdas before Compose. See Kotlin Android Development for traditional Views-based UI.
2Composable Functions
A function marked with @Composable describes UI. Composables can call other composables, accept parameters, and re-run when their inputs change.
Basic composable structure
ProfileCard composable@Composable
fun ProfileCard(
name: String,
email: String,
onEditClick: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = name, style = MaterialTheme.typography.headlineSmall)
Spacer(modifier = Modifier.height(4.dp))
Text(text = email, style = MaterialTheme.typography.bodyMedium)
Spacer(modifier = Modifier.height(12.dp))
Button(onClick = onEditClick) {
Text("Edit Profile")
}
}
}
}
Modifier — style and layout
Modifier chainText(
text = "Styled Text",
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.background(Color.LightGray, shape = RoundedCornerShape(8.dp))
.clickable { /* handle click */ }
.padding(12.dp),
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
Preview in Android Studio
@Preview annotation@Preview(showBackground = true, name = "Light Mode")
@Composable
fun ProfileCardPreview() {
MyAppTheme {
ProfileCard(
name = "Nikhil",
email = "nikhil@example.com",
onEditClick = {}
)
}
}
Composable rules
| Rule | Explanation |
| Call from composables only | setContent { } or another @Composable |
| Recomposition | Re-runs when state/params change |
| Default parameter order | Put modifier: Modifier = Modifier first optional param |
| No return value | Composables emit UI, don't return widgets |
Theme wrapper: Wrap previews and screens in MaterialTheme { } for colors, typography, and shapes — define in a Theme.kt file.
3State Management
Compose UI updates when state changes. Use remember for local UI state, state hoisting for reusable components, and ViewModel + StateFlow for screen-level data.
Local state with remember
Counter composable@Composable
fun Counter() {
var count by remember { mutableIntStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Count: $count", fontSize = 24.sp)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
State hoisting
Move state up to a parent composable so child composables stay stateless and reusable:
Hoisted state pattern// Stateless child — receives value + callback
@Composable
fun NameField(name: String, onNameChange: (String) -> Unit) {
OutlinedTextField(
value = name,
onValueChange = onNameChange,
label = { Text("Your name") },
modifier = Modifier.fillMaxWidth()
)
}
// Stateful parent owns the state
@Composable
fun RegistrationScreen() {
var name by remember { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp)) {
NameField(name = name, onNameChange = { name = it })
Spacer(modifier = Modifier.height(16.dp))
Text("Hello, ${name.ifEmpty { "Guest" }}!")
}
}
ViewModel + StateFlow
ViewModel and collectAsStateclass HomeViewModel : ViewModel() {
private val _uiState = MutableStateFlow(HomeUiState())
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
fun loadData() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
// fetch from repository...
_uiState.update { it.copy(isLoading = false, message = "Loaded") }
}
}
}
data class HomeUiState(
val message: String = "",
val isLoading: Boolean = false
)
@Composable
fun HomeScreen(viewModel: HomeViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
if (uiState.isLoading) {
CircularProgressIndicator()
} else {
Text(uiState.message)
}
}
| State API | Scope | Use for |
remember { mutableStateOf() } | Composable | Toggle, counter, expand/collapse |
rememberSaveable | Composable | Survives rotation / process death |
ViewModel + StateFlow | Screen | API data, business logic |
derivedStateOf | Composable | Computed values from other state |
Coroutines in Compose
Use viewModelScope.launch for async work — pairs naturally with Compose state. Review Kotlin Coroutines.
4UI Components
Compose provides layout composables and Material Design components for building screens — text, buttons, inputs, lists, and app structure.
Layout composables
Column, Row, Box@Composable
fun LoginForm() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Sign In", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(24.dp))
OutlinedTextField(value = "", onValueChange = {}, label = { Text("Email") })
Spacer(modifier = Modifier.height(12.dp))
Button(onClick = {}, modifier = Modifier.fillMaxWidth()) {
Text("Login")
}
}
}
Common Material components
| Component | Purpose |
Text() | Display text with typography styles |
Button() / TextButton() | Clickable actions |
OutlinedTextField() | Text input with label |
Card() | Elevated container for grouped content |
Icon() / IconButton() | Icons from Material Icons |
Switch() / Checkbox() | Boolean toggles |
LazyColumn() | Efficient scrollable list |
Scaffold() | TopBar, FAB, bottom bar structure |
LazyColumn — scrollable list
Task list@Composable
fun TaskList(tasks: List<String>, onTaskClick: (String) -> Unit) {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(tasks, key = { it }) { task ->
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onTaskClick(task) }
) {
Text(text = task, modifier = Modifier.padding(16.dp))
}
}
}
}
Scaffold — app screen structure
Scaffold with TopAppBar@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TaskScreen(onAddClick: () -> Unit) {
Scaffold(
topBar = {
TopAppBar(title = { Text("My Tasks") })
},
floatingActionButton = {
FloatingActionButton(onClick = onAddClick) {
Icon(Icons.Default.Add, contentDescription = "Add task")
}
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
TaskList(
tasks = listOf("Learn Compose", "Build app"),
onTaskClick = {}
)
}
}
}
Layout mapping from XML
| XML | Compose |
| LinearLayout (vertical) | Column |
| LinearLayout (horizontal) | Row |
| FrameLayout | Box |
| ScrollView + list | LazyColumn |
| Toolbar | TopAppBar in Scaffold |
Deep dive: For navigation, theming, and advanced Compose patterns, see the full
Android Jetpack Compose tutorial in the Android track.
5Summary Cheatsheet
| Topic | Key Takeaway |
| Introduction | Declarative UI in Kotlin; setContent { }; no XML layouts |
| Composable Functions | @Composable; Modifier; @Preview; recomposition |
| State Management | remember, state hoisting, ViewModel + StateFlow |
| UI Components | Column/Row/Box, Text, Button, LazyColumn, Scaffold |
| Material3 | MaterialTheme for colors and typography |
| Next lesson | Kotlin Projects |