Jetpack Compose

Build modern Android UIs declaratively with Kotlin — no XML layouts required

Composables Layouts State Navigation

Table of Contents

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 / ViewsJetpack Compose
TextViewText()
ButtonButton { Text("Click") }
LinearLayoutColumn / Row
RecyclerViewLazyColumn / LazyRow
activity_main.xml@Composable fun MainScreen()

1Compose 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.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.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") implementation("androidx.activity:activity-compose:1.8.2") }

MainActivity with setContent

Kotlin — MainActivity.ktclass 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") } }

2Composable 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 usageText( 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)) } }

3Layouts 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 LayoutCompose EquivalentUse Case
LinearLayout (vertical)ColumnStack items vertically
LinearLayout (horizontal)RowPlace items side by side
FrameLayoutBoxOverlay, center content
ScrollView + LinearLayoutLazyColumnLong scrollable lists
ConstraintLayoutConstraintLayout (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 = {} ) } } }

4State 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.ktclass 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 TypeScopeWhen to Use
remember { mutableStateOf() }ComposableCheckbox, text field, expand/collapse
rememberSaveableComposableSurvives configuration change / process death
ViewModel + StateFlowScreenAPI data, business logic
CompositionLocalApp-wideTheme, user session, navigation

5Compose 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.ktsdependencies { 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.ktclass 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

ActionCode
Navigate forwardnavController.navigate("detail/42")
Go backnavController.popBackStack()
Clear back stacknavController.navigate("home") { popUpTo(0) }
Pass object (type-safe)Use Kotlin Serialization + typed routes

6Hands-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?

ASQL database
BDeclarative UI toolkit for Android
CHTTP library
DGPS service
Explanation: Build UI with Kotlin functions instead of XML.
2

@Composable annotates?

AServices
BDatabase entities
CUI functions that describe screen
DManifest
Explanation: Composable functions can call other Composables.
3

remember {} used for?

ASQL query
BPermanent disk storage
CNetwork cache
DRetain value across recompositions
Explanation: remember survives recomposition within same composition.
4

State drives?

ARecomposition when value changes
BGradle sync
CDex merge
DManifest merge
Explanation: MutableState triggers UI redraw on change.
5

Column in Compose?

AHorizontal only
BVertical layout composable
CDatabase column
DNetwork stack
Explanation: Column stacks children vertically; Row horizontally.
6

Modifier in Compose?

APermission
BGradle script
CChain styling and layout behavior
DAPK signature
Explanation: Modifier.fillMaxWidth().padding(16.dp).clickable {}
7

Text composable displays?

ABluetooth devices
BVideo only
CSQL results
DText on screen
Explanation: Text("Hello", style = MaterialTheme.typography.bodyLarge)
8

Scaffold provides?

AMaterial structure — app bar, FAB, snackbar
BDatabase schema
CNetwork client
DSensor access
Explanation: Standard Material app layout shell.
9

Navigation Compose uses?

AFragmentManager only
BNavHost and NavController
CIntent filters only
DRoom DAO
Explanation: Compose Navigation with composable destinations.
10

Preview annotation?

AProGuard keep
BRuns on device only
CShows Composable in Android Studio design panel
DLint suppress
Explanation: @Preview shows UI without running full app.

10 Advanced Jetpack Compose MCQs

11

rememberSaveable?

ASame as remember always
BSurvives config change and process death via SavedState
CDisk database
DNetwork only
Explanation: Uses BundleSaver for primitive state.
12

SideEffect vs LaunchedEffect?

ABoth SQL
BIdentical
CLaunchedEffect runs suspend block on key change; SideEffect publishes to non-Compose
DBoth banned
Explanation: LaunchedEffect for coroutines tied to composition lifecycle.
13

State hoisting?

ARoom only
BStore in Application always
CStatic globals
DLift state up to parent; child receives value + callback
Explanation: Unidirectional data flow — stateless child composables.
14

LazyColumn?

AEfficient scrollable list like RecyclerView
BFixed column SQL
CNetwork queue
DService list
Explanation: items(list) { item -> ItemRow(item) }
15

collectAsStateWithLifecycle?

AMain thread block
BCollect Flow/StateFlow respecting lifecycle
CDeprecated
DXML only
Explanation: Bridge ViewModel StateFlow to Compose state.
16

CompositionLocal?

ASQL local
BGPS local
CImplicit context passed down tree (Theme, Density)
DGradle local
Explanation: LocalContentColor, MaterialTheme use CompositionLocal.
17

Recomposition skipped when?

AOnly on rotate
BNever
CEvery frame always
DState unchanged and composable stable/skipped
Explanation: Compiler skips if inputs unchanged (smart recomposition).
18

Accompanist (legacy) role?

AFill gaps before official APIs (permissions, pager)
BReplaces Compose
CDatabase
DFirebase
Explanation: Many migrated into official Compose libraries.
19

Interoperability with Views?

AImpossible
BAndroidView and ComposeView embed each other
COne or other only
DRequires Java
Explanation: Migrate screens gradually XML ↔ Compose.
20

Material3 in Compose?

ANot available
BDeprecated Material2 only
CMaterialTheme colorScheme typography shapes
DXML only
Explanation: MaterialTheme { } wraps app with design tokens.
Click an option to select, then check answers.

Jetpack Compose Interview Q&A

15 topic-focused questions for interviews and revision.

1What is Jetpack Compose?easy
Answer: Modern declarative UI toolkit — write Kotlin functions describing UI; framework handles updates.
2Basic Composable example.easy
Answer: @Composable fun Greeting(name: String) { Text(text = "Hello $name") }
3remember vs rememberSaveable.medium
Answer: remember: recomposition only. rememberSaveable: survives rotation/process death.
4State hoisting explained.medium
Answer: Parent owns state; child receives value and onValueChange callback — reusable stateless UI.
5Column vs Row vs Box.easy
Answer: Column vertical, Row horizontal, Box stacks/overlays children.
6Navigate in Compose.medium
Answer: NavHost(navController, start) { composable("home") { HomeScreen() } }
7Observe ViewModel in Compose.medium
Answer: val state by viewModel.uiState.collectAsStateWithLifecycle()
8LazyColumn vs Column.easy
Answer: Column lays out all children; LazyColumn virtualizes like RecyclerView for long lists.
9LaunchedEffect use case.medium
Answer: Load data once when composable enters: LaunchedEffect(key) { viewModel.load() }
10Modifier order matters?medium
Answer: Yes — padding then clickable differs from clickable then padding for touch area.
11Preview multiple configurations.easy
Answer: @Preview(name, uiMode, device) annotations on same composable.
12Compose vs XML when to choose.medium
Answer: New screens Compose; legacy XML fine; Google recommends Compose for new UI.
13Theme in Compose.medium
Answer: MaterialTheme(colorScheme, typography) { AppContent() } — dark/light via dynamic color.
14Testing Compose.hard
Answer: createComposeRule { setContent { MyScreen() }; onNodeWithText("OK").performClick() }
15Performance — avoid recomposition.hard
Answer: Stable types, remember, derivedStateOf, keys in LazyColumn, hoist state.