Jetpack Compose
Build modern Android UIs declaratively with Kotlin — no XML layouts required
Composables
Layouts
State
Navigation
Compose Overview
Jetpack Compose is Android's modern toolkit for building native UI using Kotlin. Instead of XML layouts, you describe what the UI should look like with @Composable functions — Compose handles rendering and updates automatically when state changes.
Imperative (XML + Views) Declarative (Compose)
───────────────────────── ─────────────────────
findViewById, setText State changes → UI recomposes
Manual visibility toggles Composable functions describe UI
Fragment transactions NavHost + composable routes
XML / Views Jetpack Compose
TextViewText()
ButtonButton { Text("Click") }
LinearLayoutColumn / Row
RecyclerViewLazyColumn / LazyRow
activity_main.xml@Composable fun MainScreen()
1 Compose Basics
To start with Compose, enable it in your project, add dependencies, and set a composable as your Activity content using setContent { }.
Gradle setup
Kotlin — app/build.gradle.kts android {
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.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.8.2")
}
MainActivity with setContent
Kotlin — MainActivity.kt class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting(name = "Android")
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello, $name!",
modifier = modifier.padding(16.dp)
)
}
Material Theme
Kotlin — Theme.kt @Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = if (darkTheme) darkColorScheme() else lightColorScheme()
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
Preview in Android Studio
Kotlin — @Preview annotation @Preview(showBackground = true, name = "Light Mode")
@Composable
fun GreetingPreview() {
MyAppTheme {
Greeting(name = "Nikhil")
}
}
2 Composable Functions
A @Composable function describes UI. It can call other composables, accept parameters, and re-run when state changes — this re-execution is called recomposition .
Building blocks
Kotlin — Common composables @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 chain
Kotlin — Modifier usage Text(
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
)
Recomposition rules
Composable functions can be called only from other composables or setContent
Functions must be stable — avoid creating new lambdas/objects unnecessarily inside composables
Compose skips recomposition when inputs haven't changed
Use remember to preserve values across recompositions
Lists with LazyColumn
Kotlin — LazyColumn for scrollable lists @Composable
fun TaskList(tasks: List<Task>, onTaskClick: (Task) -> Unit) {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(tasks, key = { it.id }) { task ->
TaskItem(task = task, onClick = { onTaskClick(task) })
}
}
}
@Composable
fun TaskItem(task: Task, onClick: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
) {
Text(text = task.title, modifier = Modifier.padding(16.dp))
}
}
3 Layouts in Compose
Compose provides layout composables to arrange UI elements — similar to LinearLayout, RelativeLayout, and ConstraintLayout in XML, but with a simpler, composable API.
Column, Row, Box
Kotlin — Basic layouts @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))
OutlinedTextField(value = "", onValueChange = {}, label = { Text("Password") })
Spacer(modifier = Modifier.height(24.dp))
Button(onClick = {}, modifier = Modifier.fillMaxWidth()) {
Text("Login")
}
}
}
Box — overlay and alignment
Kotlin — Box with overlay badge @Composable
fun AvatarWithBadge(imageUrl: String, unreadCount: Int) {
Box {
AsyncImage(
model = imageUrl,
contentDescription = "Profile",
modifier = Modifier
.size(64.dp)
.clip(CircleShape)
)
if (unreadCount > 0) {
Badge(
modifier = Modifier.align(Alignment.TopEnd)
) {
Text(unreadCount.toString())
}
}
}
}
Layout comparison
XML Layout Compose Equivalent Use Case
LinearLayout (vertical) ColumnStack items vertically
LinearLayout (horizontal) RowPlace items side by side
FrameLayout BoxOverlay, center content
ScrollView + LinearLayout LazyColumnLong scrollable lists
ConstraintLayout ConstraintLayout (Compose)Complex responsive layouts
Scaffold — app structure
Kotlin — Scaffold with TopBar and FAB @Composable
fun MainScreen(onAddClick: () -> Unit) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("My Tasks") },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = Color.White
)
)
},
floatingActionButton = {
FloatingActionButton(onClick = onAddClick) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
TaskList(
tasks = emptyList(),
onTaskClick = {}
)
}
}
}
4 State Management
Compose UI updates when state changes. Use remember for local UI state, mutableStateOf for observable values, and ViewModel + StateFlow for screen-level state.
Local state with remember
Kotlin — Counter with remember @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
Kotlin — Hoist state to parent // Stateless — receives state and 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
Kotlin — TaskViewModel.kt class TaskViewModel : ViewModel() {
private val _uiState = MutableStateFlow(TaskUiState())
val uiState: StateFlow<TaskUiState> = _uiState.asStateFlow()
fun loadTasks() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
repository.getTasks()
.onSuccess { tasks ->
_uiState.update { it.copy(tasks = tasks, isLoading = false) }
}
.onFailure { e ->
_uiState.update { it.copy(error = e.message, isLoading = false) }
}
}
}
}
data class TaskUiState(
val tasks: List<Task> = emptyList(),
val isLoading: Boolean = false,
val error: String? = null
)
Kotlin — Collect state in composable @Composable
fun TaskScreen(viewModel: TaskViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
when {
uiState.isLoading -> CircularProgressIndicator()
uiState.error != null -> Text("Error: ${uiState.error}")
else -> TaskList(tasks = uiState.tasks, onTaskClick = {})
}
}
State types summary
State Type Scope When to Use
remember { mutableStateOf() }Composable Checkbox, text field, expand/collapse
rememberSaveableComposable Survives configuration change / process death
ViewModel + StateFlowScreen API data, business logic
CompositionLocalApp-wide Theme, user session, navigation
5 Compose Navigation
Navigation Compose handles screen transitions within a Compose app. Define routes in a NavHost, navigate with a NavController, and pass arguments between screens.
Gradle dependency
Kotlin — app/build.gradle.kts dependencies {
implementation("androidx.navigation:navigation-compose:2.7.7")
}
NavHost setup
Kotlin — AppNavigation.kt @Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onNavigateToDetail = { taskId ->
navController.navigate("detail/$taskId")
},
onNavigateToSettings = {
navController.navigate("settings")
}
)
}
composable(
route = "detail/{taskId}",
arguments = listOf(navArgument("taskId") { type = NavType.IntType })
) { backStackEntry ->
val taskId = backStackEntry.arguments?.getInt("taskId") ?: 0
DetailScreen(
taskId = taskId,
onBack = { navController.popBackStack() }
)
}
composable("settings") {
SettingsScreen(onBack = { navController.popBackStack() })
}
}
}
Navigation in MainActivity
Kotlin — MainActivity.kt class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
AppNavigation()
}
}
}
}
Bottom navigation bar
Kotlin — Bottom nav with NavHost @Composable
fun MainApp() {
val navController = rememberNavController()
val items = listOf("home", "search", "profile")
Scaffold(
bottomBar = {
NavigationBar {
items.forEach { route ->
NavigationBarItem(
icon = { Icon(getIcon(route), contentDescription = route) },
label = { Text(route.replaceFirstChar { it.uppercase() }) },
selected = navController.currentDestination?.route == route,
onClick = {
navController.navigate(route) {
popUpTo("home") { saveState = true }
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(
navController = navController,
startDestination = "home",
modifier = Modifier.padding(innerPadding)
) {
composable("home") { HomeScreen() }
composable("search") { SearchScreen() }
composable("profile") { ProfileScreen() }
}
}
}
Navigation patterns
Action Code
Navigate forward navController.navigate("detail/42")
Go back navController.popBackStack()
Clear back stack navController.navigate("home") { popUpTo(0) }
Pass object (type-safe) Use Kotlin Serialization + typed routes
6 Hands-On Exercises
1
Enable Compose in a new project. Create a Greeting composable and preview it with @Preview.
2
Build a profile card using composable functions — Card, Text, Button, and Modifier padding.
3
Create a login screen with Column , OutlinedTextField, and a full-width Button inside a Scaffold.
4
Implement a counter with state management — use remember and hoist state to a parent composable.
5
Connect a ViewModel with StateFlow to a LazyColumn task list. Show loading and error states.
6
Set up Compose Navigation with Home and Detail screens. Pass a task ID as a route argument.
7
Bonus: Add a bottom navigation bar with three tabs and preserve state when switching between them.
Jetpack Compose MCQ Practice
10 Basic MCQs
10 Advanced MCQs
10 Basic Jetpack Compose MCQs
1
Jetpack Compose is?
A SQL database
B Declarative UI toolkit for Android
C HTTP library
D GPS service
Explanation: Build UI with Kotlin functions instead of XML.
2
@Composable annotates?
A Services
B Database entities
C UI functions that describe screen
D Manifest
Explanation: Composable functions can call other Composables.
3
remember {} used for?
A SQL query
B Permanent disk storage
C Network cache
D Retain value across recompositions
Explanation: remember survives recomposition within same composition.
4
State drives?
A Recomposition when value changes
B Gradle sync
C Dex merge
D Manifest merge
Explanation: MutableState triggers UI redraw on change.
5
Column in Compose?
A Horizontal only
B Vertical layout composable
C Database column
D Network stack
Explanation: Column stacks children vertically; Row horizontally.
6
Modifier in Compose?
A Permission
B Gradle script
C Chain styling and layout behavior
D APK signature
Explanation: Modifier.fillMaxWidth().padding(16.dp).clickable {}
7
Text composable displays?
A Bluetooth devices
B Video only
C SQL results
D Text on screen
Explanation: Text("Hello", style = MaterialTheme.typography.bodyLarge)
8
Scaffold provides?
A Material structure — app bar, FAB, snackbar
B Database schema
C Network client
D Sensor access
Explanation: Standard Material app layout shell.
9
Navigation Compose uses?
A FragmentManager only
B NavHost and NavController
C Intent filters only
D Room DAO
Explanation: Compose Navigation with composable destinations.
10
Preview annotation?
A ProGuard keep
B Runs on device only
C Shows Composable in Android Studio design panel
D Lint suppress
Explanation: @Preview shows UI without running full app.
10 Advanced Jetpack Compose MCQs
11
rememberSaveable?
A Same as remember always
B Survives config change and process death via SavedState
C Disk database
D Network only
Explanation: Uses BundleSaver for primitive state.
12
SideEffect vs LaunchedEffect?
A Both SQL
B Identical
C LaunchedEffect runs suspend block on key change; SideEffect publishes to non-Compose
D Both banned
Explanation: LaunchedEffect for coroutines tied to composition lifecycle.
13
State hoisting?
A Room only
B Store in Application always
C Static globals
D Lift state up to parent; child receives value + callback
Explanation: Unidirectional data flow — stateless child composables.
14
LazyColumn?
A Efficient scrollable list like RecyclerView
B Fixed column SQL
C Network queue
D Service list
Explanation: items(list) { item -> ItemRow(item) }
15
collectAsStateWithLifecycle?
A Main thread block
B Collect Flow/StateFlow respecting lifecycle
C Deprecated
D XML only
Explanation: Bridge ViewModel StateFlow to Compose state.
16
CompositionLocal?
A SQL local
B GPS local
C Implicit context passed down tree (Theme, Density)
D Gradle local
Explanation: LocalContentColor, MaterialTheme use CompositionLocal.
17
Recomposition skipped when?
A Only on rotate
B Never
C Every frame always
D State unchanged and composable stable/skipped
Explanation: Compiler skips if inputs unchanged (smart recomposition).
18
Accompanist (legacy) role?
A Fill gaps before official APIs (permissions, pager)
B Replaces Compose
C Database
D Firebase
Explanation: Many migrated into official Compose libraries.
19
Interoperability with Views?
A Impossible
B AndroidView and ComposeView embed each other
C One or other only
D Requires Java
Explanation: Migrate screens gradually XML ↔ Compose.
20
Material3 in Compose?
A Not available
B Deprecated Material2 only
C MaterialTheme colorScheme typography shapes
D XML only
Explanation: MaterialTheme { } wraps app with design tokens.
Check All Answers
Click an option to select, then check answers.
Jetpack Compose Interview Q&A
15 topic-focused questions for interviews and revision.
1 What is Jetpack Compose?easy
Answer: Modern declarative UI toolkit — write Kotlin functions describing UI; framework handles updates.
2 Basic Composable example.easy
Answer: @Composable fun Greeting(name: String) { Text(text = "Hello $name") }
3 remember vs rememberSaveable.medium
Answer: remember: recomposition only. rememberSaveable: survives rotation/process death.
4 State hoisting explained.medium
Answer: Parent owns state; child receives value and onValueChange callback — reusable stateless UI.
5 Column vs Row vs Box.easy
Answer: Column vertical, Row horizontal, Box stacks/overlays children.
6 Navigate in Compose.medium
Answer: NavHost(navController, start) { composable("home") { HomeScreen() } }
7 Observe ViewModel in Compose.medium
Answer: val state by viewModel.uiState.collectAsStateWithLifecycle()
8 LazyColumn vs Column.easy
Answer: Column lays out all children; LazyColumn virtualizes like RecyclerView for long lists.
9 LaunchedEffect use case.medium
Answer: Load data once when composable enters: LaunchedEffect(key) { viewModel.load() }
10 Modifier order matters?medium
Answer: Yes — padding then clickable differs from clickable then padding for touch area.
11 Preview multiple configurations.easy
Answer: @Preview(name, uiMode, device) annotations on same composable.
12 Compose vs XML when to choose.medium
Answer: New screens Compose; legacy XML fine; Google recommends Compose for new UI.
13 Theme in Compose.medium
Answer: MaterialTheme(colorScheme, typography) { AppContent() } — dark/light via dynamic color.
14 Testing Compose.hard
Answer: createComposeRule { setContent { MyScreen() }; onNodeWithText("OK").performClick() }
15 Performance — avoid recomposition.hard
Answer: Stable types, remember, derivedStateOf, keys in LazyColumn, hoist state.