Jetpack Compose Interview Questions for Android App Development
25 Jetpack Compose questions with answers for android app development roles — 10 easy, 11 medium, 4 hard.
About Jetpack Compose
Jetpack Compose is Android's declarative UI toolkit and is now the default for new screens. Learn composables, state, recomposition, state hoisting, side effects, lazy lists, theming with Material 3, adaptive layouts, accessibility and Compose testing. Interviewers often ask you to build a small screen and explain how recomposition works.
Sheet progressStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
✓ 3 questions free • Unlock the rest in seconds
01
What is Jetpack Compose and how is it different from XML views?
Jetpack Compose is Android's modern declarative UI toolkit. You describe the UI as Kotlin functions of state, and Compose redraws the parts that change when state changes. With XML views you inflate layouts and imperatively update views with findViewById or binding. Compose removes adapters and much boilerplate, makes theming and previews easier, and interoperates with views through ComposeView and AndroidView, allowing gradual migration.
Recomposition is Compose re-running composable functions whose inputs or read state changed, to update the UI. Compose tries to skip composables whose parameters are stable and unchanged. Composables can run often and in any order, so they must be side-effect free and fast; do not make network calls or write to variables inside them. Use Layout Inspector recomposition counts to find composables recomposing more than expected.
03
What is the difference between remember and rememberSaveable?
remember keeps a value across recompositions but loses it when the composable leaves composition, on configuration change or process death. rememberSaveable stores the value in the saved instance state Bundle, so it survives rotation and process death; it works with types that can go in a Bundle or with a custom Saver. Use rememberSaveable for things like text input or selected tab, and a ViewModel for screen-level data.
State hoisting means moving state out of a composable to its caller, and passing the value plus event callbacks down, like value and onValueChange. The composable becomes stateless, reusable and easy to preview and test. State should be hoisted to the lowest common parent that needs to read or change it, and screen-level state usually lives in a ViewModel. This creates unidirectional data flow: state flows down, events flow up.
05
Write a stateless text field with a hoisted state and validation message.
Modifiers are applied in a chain from left to right, each wrapping the next. Modifier.padding(16.dp).background(Color.Red) adds padding outside the red area, while background first then padding colours the padding too. Similarly, clickable before padding makes the whole padded area clickable. Thinking of each modifier as wrapping the content after it helps predict layout, size and touch target behaviour.
08
How do you collect a ViewModel StateFlow in a composable?
Use val state by viewModel.uiState.collectAsStateWithLifecycle() from lifecycle-runtime-compose. It collects only while the lifecycle is at least STARTED, saving resources in the background, unlike collectAsState which keeps collecting. Pass the resulting state and callbacks to a stateless screen composable, which keeps the screen previewable and testable without a ViewModel. Obtain the ViewModel with hiltViewModel() or viewModel() at the route level.
09
A composable makes an API call every time the screen recomposes. What is wrong and how do you fix it?
The call is placed directly in the composable body, which runs on every recomposition. Composables must be side-effect free. Move loading to the ViewModel, triggered in init or by a user event, and expose the result as StateFlow. If something must start from composition, wrap it in LaunchedEffect with a stable key like the item ID, so it runs once per key rather than on every recomposition.
10
A text field loses its typed value after rotation. How do you fix it?
The value is probably held with remember, which is lost when the Activity is recreated. Either use rememberSaveable for local UI state, or keep the text in the ViewModel and pass it down with onValueChange. For data that must survive process death too, store it in SavedStateHandle in the ViewModel. Avoid storing text field state asynchronously, because delayed updates can cause cursor jumps.
11
Explain LaunchedEffect, DisposableEffect and SideEffect.
LaunchedEffect(key) launches a coroutine when it enters composition and restarts when the key changes, used for one-time loads or showing a snackbar. DisposableEffect(key) runs setup and requires an onDispose block for cleanup, used for registering listeners or lifecycle observers. SideEffect runs after every successful recomposition to publish Compose state to non-Compose code, such as analytics. Choose keys carefully so effects do not restart unexpectedly.
12
What does derivedStateOf do and when should you use it?
derivedStateOf creates state computed from other state, and only triggers recomposition when the computed result changes. A classic example is showing a scroll-to-top button: val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } }. Without it, every scroll pixel would recompose. Do not use it when the result changes as often as the input, because it only adds overhead.
13
How do LazyColumn keys improve performance and correctness?
By default, items are identified by position, so inserting or deleting an item can make Compose reuse state for the wrong item and recompose more than needed. Supplying key = { it.id } gives each item a stable identity, so remembered state, animations with animateItem and scroll position follow the right item. Also supply contentType for mixed lists so compositions are reused efficiently. Keys must be unique and Bundle-saveable.
14
What is CompositionLocal and when should you use it?
CompositionLocal passes data implicitly down the tree without threading it through every parameter, like LocalContext, LocalDensity or MaterialTheme values. Create one with compositionLocalOf when values change often, or staticCompositionLocalOf when they rarely change. Use it for cross-cutting values like theme or analytics loggers, not for regular screen data, because implicit dependencies make composables harder to understand and test.
15
How do you implement theming with Material 3 in Compose?
Wrap the app in a MaterialTheme with a ColorScheme, Typography and Shapes. Use lightColorScheme and darkColorScheme, pick based on isSystemInDarkTheme(), and use dynamicLightColorScheme or dynamicDarkColorScheme on Android 12 and above for Material You colours. Components read MaterialTheme.colorScheme.primary and similar roles, so avoid hardcoding colours. Extend the theme with a custom CompositionLocal for brand-specific tokens.
Use the Compose testing APIs with createComposeRule or createAndroidComposeRule. Find nodes by text, content description or testTag, then perform actions and assertions, like onNodeWithText("Login").performClick() and onNodeWithTag("error").assertIsDisplayed(). Test stateless composables with fake state for speed. Tests synchronise with Compose automatically. Screenshot testing tools like Roborazzi or Paparazzi catch visual regressions without a device.
17
Write a Compose UI test that checks an error appears when login is clicked with an empty email.
To use Compose inside views, add a ComposeView to an XML layout or Fragment and call setContent, setting a ViewCompositionStrategy suited to the lifecycle. To use a view inside Compose, wrap it with AndroidView(factory = { MapView(it) }, update = { }). This allows migrating screen by screen, starting with new screens or leaf components. Share the theme using a Compose theme adapter or by mirroring tokens.
19
Your team wants to migrate a large XML app to Compose. How would you plan it?
Start by setting up a shared Compose theme matching the existing design system, then build new screens in Compose. Migrate leaf components and simple screens next, using ComposeView inside fragments, and leave complex custom views wrapped in AndroidView for later. Measure performance with Macrobenchmark and add Baseline Profiles. Add Compose UI and screenshot tests, train the team, and set a rule that new UI is Compose-only.
20
Tell me about a time you disagreed with a designer about a UI decision.
Situation: a designer wanted a custom tiny close icon on a full-screen dialog. Task: keep the app accessible without blocking the design. Action: I showed that the touch target was below the 48dp guideline and that TalkBack had no label, and prototyped the same look with a larger invisible touch area. Result: we kept the visual style, passed accessibility checks, and agreed to review touch targets in future design handoffs.
Give icons and images a meaningful contentDescription, or null when decorative. Keep touch targets at least 48dp, which Material components handle by default. Use semantics modifiers to merge descendants for list items, mark headings with heading(), and describe custom actions. Support font scaling by using sp and avoiding fixed heights for text. Check colour contrast and test with TalkBack and the Accessibility Scanner.
Compose skips recomposing a composable when all its parameters are stable and equal to before. Primitives, String, functions and data classes with val stable properties are stable. Classes with var properties, standard List or types from modules without the Compose compiler may be unstable, preventing skipping. Since strong skipping mode became default in Kotlin 2.0.20, unstable parameters are compared by instance, reducing the problem, but immutable models still help.
23
A Compose list scrolls with jank on low-end phones. How do you investigate and fix it?
Test a release build with R8, since debug builds are much slower. Use Layout Inspector recomposition counts and a system trace or Macrobenchmark to find hot spots. Common fixes: add keys and contentType, avoid heavy work or sorting inside composables, move computation to the ViewModel, use derivedStateOf for scroll-based state, defer state reads with lambda modifiers like offset { }, load images at the right size with Coil, and add a Baseline Profile.
24
How do you build an adaptive layout for phones, foldables and tablets in Compose?
Use WindowSizeClass from the Material 3 adaptive library to choose layouts by width class, not device type. On compact width show a bottom navigation bar; on medium use a navigation rail; on expanded use a list-detail layout with ListDetailPaneScaffold or NavigationSuiteScaffold. Avoid locking orientation, since Android 16 ignores orientation restrictions on large screens. Test with resizable emulators and foldable postures.
Each frame goes through composition, which decides what UI to show by running composables; layout, which measures and places each element; and drawing, which renders pixels. State read in a phase only invalidates that phase and later ones. Reading scroll state inside Modifier.offset { } or drawBehind defers the read to layout or draw, skipping recomposition entirely, which is a key optimisation for animations and scrolling effects.
Frequently asked questions
How important is Jetpack Compose in Android App Development interviews?
Jetpack Compose is Android's declarative UI toolkit and is now the default for new screens. Learn composables, state, recomposition, state hoisting, side effects, lazy lists, theming with Material 3, adaptive layouts, accessibility and Compose testing. Interviewers often ask you to build a small screen and explain how recomposition works.
How should I use the Jetpack Compose sheet?
Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.