Jetpack Compose: What It Is, Key Concepts and Composable Functions

Author: IT Sectr Published: 2026-05-01 Reading time: 9 min

Jetpack Compose is a modern declarative toolkit for building Android interfaces in Kotlin. The developer describes the UI through composable functions, and the toolkit automatically redraws only the changed parts. According to Android Developers (2026), Jetpack Compose runs on Android 5.0 (API 21) and above, supports Material Design 3, and achieves 120 FPS on mid-range devices thanks to its own Recomposition system — a smart diff algorithm that updates only the changed widgets.

Key Takeaways

  • Jetpack Compose — a declarative UI framework for Android where the interface is built through annotated @Composable functions in Kotlin.
  • Recomposition — a mechanism that automatically updates only those components whose data has changed, delivering 120 FPS.
  • State is managed via mutableStateOf, collectAsState and StateFlow — when the value changes, composition restarts for dependent views.
  • Modifier — a chain of functions for configuring padding, sizes, background, clicks and animations without class inheritance.
  • Side Effects — LaunchedEffect, DisposableEffect and rememberCoroutineScope — manage side actions: timers, network requests, subscriptions.

What is Jetpack Compose?

Jetpack Compose is a declarative framework from Google for building Android user interfaces, announced in 2019 and reaching stable release in 2021. Unlike the old View System (XML layout + Activity/Fragment), Compose uses annotated Kotlin functions — @Composable. The interface is described entirely in Kotlin: there is no separation between XML and code. This eliminated the class of errors related to mismatched IDs in XML and Kotlin (type-safe synthetic did not help with refactoring).

Compose is built on its own rendering system — Canvas, not tied to the View hierarchy. Each Composable draws itself on Canvas directly, bypassing onMeasure/onDraw of the View System. This provides a performance boost on complex screens: in Google tests (2023), a Compose screen with 200 elements rendered 40% faster than a similar one on RecyclerView + ViewHolder.

Minimum Requirements and Compatibility

Compose requires minSdk 21 (Android 5.0) and Kotlin 1.9+. The Compose BOM (Bill of Materials) synchronizes the versions of all Compose libraries. The framework is compatible with existing View System code: Compose is embedded via ComposeView in XML layouts, and old Views via AndroidView in the Compose hierarchy. According to Google Play Console (2025), Android 5.0+ covers 97% of active devices, so compatibility is not a limitation for most projects.

Composable Functions and Composition

@Composable is an annotation that turns a regular Kotlin function into a UI building block. A Composable function describes how a piece of the interface should look — text, button, list. Instead of returning a value, the function emits UI components into the composition. It is similar to a generator: each function adds elements to the screen when called.

kotlin
@Composable
fun ProfileCard(name: String, avatarUrl: String) {
    Card(
        modifier = Modifier.fillMaxWidth().padding(16.dp),
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.surface
        )
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) {
            AsyncImage(
                model = avatarUrl,
                contentDescription = "Avatar",
                modifier = Modifier.size(48.dp).clip(CircleShape)
            )
            Spacer(Modifier.width(12.dp))
            Text(
                text = name,
                style = MaterialTheme.typography.titleMedium
            )
        }
    }
}

The ProfileCard function takes parameters (name, avatarUrl) and emits Card → Row → AsyncImage + Text. Composition is the tree of emitted components in a single pass. If the parameters have not changed, Compose skips the function call (recomposition skip). If only name has changed, only Text will be called, the rest of the elements will not be redrawn. This intelligent recomposition is the key performance advantage of Compose over manual View System optimization.

Slots and Content Lambda

Composable functions actively use slots — trailing lambda, content: @Composable (() -> Unit). This allows creating containers: Card, Column, Row accept a content lambda, and the content is embedded in the slot. The Slot API replaced XML attributes like android:layout_gravity — now child element positioning is set by Kotlin code inside the content block.

State Management in Compose

State in Compose is any value that can change over time. When the state changes, Compose schedules recomposition for all components that read this state. The mechanism resembles React hooks: mutableStateOf returns MutableState<T>, reading .value automatically subscribes the current composition to changes.

kotlin
@Composable
fun CounterExample() {
    var count by remember { mutableStateOf(0) }

    Column(modifier = Modifier.padding(16.dp)) {
        Text("Clicked: $count")
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}

@Composable
fun UserScreen(viewModel: UserViewModel) {
    val userName by viewModel.userName.collectAsState()
    Text("User: $userName")
}

remember preserves the value between recompositions — otherwise mutableStateOf would be created anew on every UI update. collectAsState() converts StateFlow from ViewModel into Compose-compatible state. Recommendation — use ViewModel with StateFlow for screen-level state, and mutableStateOf for local state (e.g., expanded card). This separation follows the smart/dumb component principle.

State Hoisting

State Hoisting is a pattern of lifting state from a child component to the parent. The parent passes the value and a callback through parameters, the child calls the callback on change. The parent holds mutableStateOf, the child only parameters. This makes the component reusable and testable: the same TextField can be used with any data source.

Modifier — Styling and Appearance

Modifier is an object that describes Composable transformations: size, padding, background, click handling, animation, scrolling. Modifiers are applied via a call chain: Modifier.fillMaxWidth().padding(16.dp).background(Color.Blue).clickable { }. Each call returns a new Modifier with the added property — no mutation of the original object.

The order of modifiers matters. Modifier.padding(16.dp).background(Color.Blue) fills the area with padding. Modifier.background(Color.Blue).padding(16.dp) fills the inner rectangle, and the padding remains transparent. The mechanics resemble the CSS box model: padding first → background works like margin + background; background first → padding works like background + inner padding. The developer just needs to remember: padding first = outer margin, padding after = inner padding.

Custom Modifiers

If built-in modifiers are insufficient, a custom one is created via Modifier.composed { ... } or Modifier.then(). Inside a custom modifier, you can use layout measurements (Modifier.layout { measurable, constraints -> ... }), drawing (Modifier.drawWithContent { ... }), gestures (Modifier.pointerInput { ... }). Example: a modifier for a pulsing animation on click — measures the size, on click starts a scale animation using animateFloatAsState.

For animations, Compose provides animate*AsState (animateFloatAsState, animateColorAsState, animateDpAsState) — values animate between old and new state on change. For enter/exit animations — AnimatedVisibility and AnimatedContent with built-in transitions (fade, slide, expand). All animations work on the graphics layer without triggering unnecessary composition.

Side Effects: LaunchedEffect, DisposableEffect, remember

Composable functions should not perform side effects directly (network requests, timers, subscriptions) — they are called on every recomposition, which would lead to duplicate requests. For side effects, Compose provides a family of Effect functions: LaunchedEffect starts a coroutine on entry into composition and cancels it on exit, DisposableEffect — for resources requiring explicit cleanup (sensors, BroadcastReceiver).

kotlin
@Composable
fun SensorReader() {
    val context = LocalContext.current
    var sensorValue by remember { mutableStateOf(0f) }

    DisposableEffect(Unit) {
        val sensor = registerSensorListener(context) { value ->
            sensorValue = value
        }
        onDispose {
            unregisterSensorListener(sensor)
        }
    }

    Text("Value: $sensorValue")
}

@Composable
fun UserGreeting(userId: String) {
    LaunchedEffect(userId) {
        val profile = api.fetchProfile(userId)
        // state update
    }
}

LaunchedEffect(userId) restarts if userId changes — the previous coroutine is canceled, a new one starts with the new userId. This eliminates manual request cancellation management. DisposableEffect(Unit) — an effect with a fixed key Unit, fires on composition entry and calls onDispose on exit. SensorReader registers a listener and unsubscribes when leaving the screen — without risk of leaks.

rememberCoroutineScope

If a coroutine needs to be launched not on composition entry but on an event (button click), use rememberCoroutineScope(). It returns a CoroutineScope tied to the Composable lifecycle, requiring no DisposableEffect. Example: launching a network request on button click — scope.launch { viewModel.loadData() }.

Jetpack Compose vs View System: Comparison

Choosing between Compose and View System is the main architectural question for Android developers in 2026. Both technologies are supported by Google, but Compose is the primary direction on which Google spends resources. View System receives only critical fixes and is not evolving. The difference manifests in syntax, state management, performance, and development time.

AspectJetpack ComposeView System
UI DescriptionKotlin @Composable functionsXML layout + Activity/Fragment
StatemutableStateOf, StateFlow, automatic redrawfindViewById, manual: setText, notifyDataSetChanged
PerformanceIntelligent recomposition, Canvas renderingView hierarchy, measure/layout/draw
Animationsanimate*AsState, AnimatedVisibility, built-inValueAnimator, ObjectAnimator, Transition
CompatibilityminSdk 21, ComposeView/AndroidView bridgesAll versions, any
APK Size+3–5 MB for ComposeNo overhead

For new projects, Google recommends Jetpack Compose as the standard for UI development. View System remains for maintaining code written before 2021, and for cases where minimal APK size is critical (e.g., for emerging markets with entry-level devices). Compose reduces UI code volume by 30–50% compared to View System thanks to its declarative syntax and built-in animation.

Frequently Asked Questions

Can I use Compose in an existing View System project?

Yes, via ComposeView in XML layout. Add the Compose dependency and wrap the screen or part of it in ComposeView { MyComposable() }. Migration is screen-by-screen.

Why does my Composable redraw too often?

The reason is that state is lifted too high or mutable objects are used. Fix: derivedStateOf for derived data and remember for stable references.

How to implement a list in Compose?

Use LazyColumn (analogous to RecyclerView). Elements are created and reused as you scroll. For complex lists with different cell types — LazyColumn { items(items, key = { it.id }) { ... } }.

Do I need to learn View System before Compose?

No, you can start directly with Compose. Knowledge of View System helps when maintaining legacy code, but Compose is a standalone ecosystem with its own documentation and patterns.

Does Compose support Material 3?

Yes, Material 3 has been the standard Compose theme since 2023. It is added via implementation("androidx.compose.material3:material3"). Material 2 is considered deprecated.

Summary

  • Jetpack Compose — a declarative UI framework for Android where the entire interface is written in Kotlin via @Composable functions.
  • Recomposition automatically redraws only changed components, delivering 120 FPS without manual optimization.
  • State is managed via mutableStateOf, collectAsState and StateFlow; the State Hoisting pattern makes components reusable.
  • Modifier — a chain of transformations for configuring appearance, animations and behavior without class inheritance.
  • Side Effects (LaunchedEffect, DisposableEffect) isolate side actions from recomposition, preventing leaks and duplicate requests.
  • LazyColumn replaces RecyclerView with less code, and AnimatedVisibility replaces complex Animator chains.
  • Google recommends Compose for all new projects; View System remains for maintaining legacy code and cases where minimal APK size is critical.

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.

Discuss the project

Read also