Jetpack Compose is a modern declarative UI framework from Google for Android development, released in stable version 1.0 in July 2021. Compose replaces imperative XML layout and View system with Kotlin functions annotated with @Composable. According to Google I/O (2025), Compose is used in 55% of new Android projects, and the average UI code size is reduced by 30–50% compared to XML. The framework automatically redraws only the changed parts of the screen when State changes, eliminating manual calls to notifyDataSetChanged or findViewById.
Key Takeaways
Jetpack Compose is a Google UI framework built on reactive programming principles. Instead of Activity with XML layout, FragmentManager, and ViewBinding, the developer describes the interface through Kotlin functions annotated with @Composable. Compose does not use LayoutInflater — UI is pure Kotlin functions compiled into bytecode. The framework consists of several libraries: Compose UI (basic components Text, Button, Column, Row), Compose Foundation (Material Design, Gestures, Focus), Compose Material3 (Material You with Dynamic Colors) and Compose Runtime (State, Side Effects, Coroutines). Compose works on Android API 21+ (Android 5.0) and is fully compatible with existing View/XML code through ComposeView — an AndroidView adapter.
View System (2008) uses a hierarchy of View objects with manual management: the developer creates an XML layout, finds Views via findViewById, implements an Adapter for RecyclerView and calls notifyDataSetChanged when data changes. Compose describes UI functionally: when state changes, the framework recomposes only the changed Composable functions, computes the diff and applies minimal changes to Canvas. Compose performance is comparable to View System, and in scenarios with complex lists (LazyColumn) it often surpasses RecyclerView by skipping unnecessary elements through the key parameter. At IT Sectr, Compose is used for new modules of Android applications with minSdk 24+.
@Composable — an annotation that turns a regular Kotlin function into a UI building block. Composable functions can call other Composable functions, forming a component tree. Unlike View, Composable functions have no state by default — they are stateless and redraw on every change of input parameters or State. The Compose Compiler transforms @Composable functions into code that can be interrupted and resumed (positional memoization) — this allows Compose to restart only the changed parts without fully rebuilding the tree.
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun UserProfile(name: String, avatarUrl: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AsyncImage(
model = avatarUrl,
contentDescription = "User avatar",
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = name,
style = MaterialTheme.typography.titleMedium
)
}
}The UserProfile function accepts name and avatarUrl as parameters, builds a row with an avatar and name. Modifier.fillMaxWidth() stretches the component to full width, .padding(16.dp) adds padding. MaterialTheme.typography.titleMedium uses the system Material 3 style. When name or avatarUrl parameters change, Compose recomposes UserProfile — recreating the component with new values.
State in Compose is any value whose change triggers recomposition (restart) of dependent @Composable functions. The basic approach is mutableStateOf(), creating a MutableState<T> with Compose's snapshot system support. Snapshot is a change tracking mechanism: when State is written, Compose marks the current Snapshot as dirty, and the recomposition scheduler restarts only the Composable functions reading that State. For observable collections use mutableStateListOf() and mutableStateMapOf(). Compose supports State from Lifecycle-Aware components via collectAsState() for Kotlin Flow and observeAsState() for LiveData.
@Composable
fun LikeButton() {
var liked by remember { mutableStateOf(false) }
var count by remember { mutableStateOf(42) }
Button(
onClick = {
liked = !liked
if (liked) count++ else count--
},
colors = ButtonDefaults.buttonColors(
containerColor = if (liked) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.surfaceVariant
)
) {
Text("♥ $count")
}
}The code creates a "Like" button with liked (Boolean) and count (Int) state stored via mutableStateOf(). On press, liked is toggled and the counter increments/decrements — Compose automatically recomposes LikeButton. The by delegate allows using State like a regular variable: liked = !liked instead of liked.value = !liked.value. The button color reactively changes between primary (liked) and surfaceVariant (not liked).
remember — a Compose function that caches a value between recompositions. Without remember, every restart of a Composable function creates a new state (mutableStateOf) — this breaks application logic: on screen rotation or parent State change, the counter resets. remember accepts a calculator lambda and computes the value only on the first composition; on subsequent ones it returns the cached result. To reset the cache use remember(key) — the key determines when to recompute the value. remember(key1, key2) recomputes when any key changes. For long-lived states (surviving screen rotation) use rememberSaveable — an analog of remember that saves to Bundle via SavedStateHandle.
@Composable
fun TimerScreen() {
var seconds by rememberSaveable { mutableStateOf(0) }
LaunchedEffect(Unit) {
while (true) {
delay(1000)
seconds++
}
}
Column(
modifier = Modifier.fillMaxSize().padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Elapsed: ${seconds}s",
style = MaterialTheme.typography.displayMedium
)
}
}rememberSaveable preserves seconds on screen rotation (configuration change). LaunchedEffect starts a coroutine that increments seconds every second. On TimerScreen recomposition (every second) seconds is not reset thanks to rememberSaveable. Use regular remember for state that should not survive configuration changes (e.g., animation flags).
Modifier — an immutable container object implementing the Builder pattern for configuring the appearance, behavior, and layout of Composable components. Each Modifier method call (.padding(), .width(), .clickable(), .background()) returns a new Modifier with the added element. Modifier order matters: .padding(16.dp).clickable { } — padding is applied before the click handler, clickable tracks the entire area including padding. .clickable { }.padding(16.dp) — the entire area before padding is clickable, padding shifts content inward. Modifier supports custom implementations via then(otherModifier) and compositionLocal for accessing parent settings.
@Composable
fun CardExample() {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clickable { println("Card clicked") },
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Jetpack Compose", style = MaterialTheme.typography.titleLarge)
Spacer(modifier = Modifier.height(8.dp))
Text(
"Declarative UI for Android with State and Modifier",
style = MaterialTheme.typography.bodyMedium
)
}
}
}CardExample demonstrates the Modifier chain: fillMaxWidth + padding + clickable. Card is a Material3 component with its own elevation and colors settings. The nested Column has internal padding of 16dp. Compose Material3 automatically adapts colors to Dynamic Color (Material You) on Android 12+. Card is one of 30+ built-in Material3 components in Compose.
State hoisting — a pattern of lifting state out of a Composable function into its caller, making the function stateless. A stateless component receives data and callbacks through parameters without owning State itself. This improves reusability and testability: the component can be called with different States and tested in preview. Google recommends the architecture: ViewModel + StateFlow (or MutableStateFlow) + collectAsState() in Compose. ViewModel lifts state to the screen level, Compose renders it. Side effects (LaunchedEffect, DisposableEffect) handle one-time events.
| Component | Responsibility | Technology |
|---|---|---|
| ViewModel | State storage, business logic | StateFlow, MutableStateFlow |
| Screen Composable | Screen organization, State hoisting | collectAsState(), Scaffold, LazyColumn |
| Stateless Component | UI without state, pure rendering | @Composable parameters + lambdas |
| Side Effect | One-time actions (snackbar, navigation) | LaunchedEffect, SnackbarHostState |
Frequently Asked Questions
XML layout is imperative: LayoutInflater loads XML, findViewById finds Views, the developer manually updates text/lists. Compose is declarative: UI is described by Kotlin @Composable functions, and the framework automatically redraws only the changed parts when State changes. Compose reduces UI code volume by 30–50% and does not use XML.
Yes, Google provides Navigation Compose — a library for declarative navigation between screens. NavController manages the route stack, composable() registers screens, navArgument passes parameters. Alternatives: Voyager (community) and Decompose (Badoo). Navigation Compose is the official standard with deep links and type-safe argument support.
Modifier is an immutable container of decoration patterns (padding, size, clickable, background, border, clip) applied to Composable components. Each call returns a new Modifier with the added element. Order matters: .padding().clickable() applies padding before click, .clickable().padding() — the opposite. Modifier is the primary way to customize Compose components.
Jetpack Compose supports Android API 21+ (Android 5.0 Lollipop) through the Compose Compiler and Compose BOM. Material 3 (Dynamic Colors) is available on Android 12+; older versions use a fallback palette. For new projects, Google recommends minSdk 24+ (Android 7.0), which is fully covered by Compose.
Google provides Compose UI Test — a library for testing on JVM (Desktop) and emulator. Key functions: composeTestRule.setContent { } to render a component, onNodeWithText() to find elements, performClick()/performTextInput() for actions. Espresso tests are compatible with Compose via ComposeTestRule, but Compose UI Test is the preferred and faster approach.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also