← Back to Articles
Android Kotlin Jetpack Compose Mobile Development UI Development Android Architecture

Jetpack Compose Complete Guide: Modern Android UI Development with Kotlin Examples and Best Practices

Learn Jetpack Compose from beginner to advanced concepts. Understand composable functions, state management, layouts, navigation, Material 3, animations, performance optimization, and modern Android UI architecture with Kotlin examples.

July 2026
30 min read
Written by Engineering Team

Introduction

Android UI development has changed significantly with the introduction of Jetpack Compose. Instead of building interfaces using XML layouts and manually updating views, Compose introduces a modern declarative approach where UI is created based on application state.

Jetpack Compose allows developers to create responsive and reusable interfaces using Kotlin code. It reduces boilerplate, improves development speed, and provides better support for modern application architectures.

Modern Android applications commonly combine Jetpack Compose with Kotlin Coroutines, ViewModel, StateFlow, Navigation Compose, Hilt dependency injection, and Clean Architecture.

Jetpack Compose UI architecture
Jetpack Compose creates UI directly from application state.

What Is Jetpack Compose?

Jetpack Compose is Android’s modern toolkit for building native user interfaces using Kotlin. It replaces traditional XML layouts with a declarative programming model.

In traditional Android development, developers manually modified views when data changed. Compose works differently: when state changes, Compose automatically updates the affected UI.

Traditional XML UI Jetpack Compose
Imperative approach Declarative approach
Separate XML files UI written in Kotlin
Manual view updates Automatic recomposition
More boilerplate Reusable components

Why Use Jetpack Compose?

  • Less code compared with XML layouts

  • Reusable UI components

  • Better Kotlin integration

  • Faster UI development

  • Powerful state management

  • Modern Material Design support

Compose also makes it easier to create adaptive interfaces that work across different screen sizes and device types.

Setting Up Jetpack Compose

New Android projects can enable Compose directly from Android Studio.

build.gradle.kts
kotlin
              android {

    buildFeatures {
        compose = true
    }


    composeOptions {

        kotlinCompilerExtensionVersion =
            "1.5.15"
    }
}

            

Compose applications require Compose libraries for UI components, Material Design, and tooling support.

Composable Functions

The fundamental building block of Jetpack Compose is the composable function. A composable describes a part of the user interface.

Greeting.kt
kotlin
              @Composable
fun Greeting(
    name: String
) {

    Text(
        text = "Hello $name"
    )
}

            

Composable functions describe what the UI should look like. Compose handles creating and updating the required UI elements.

Composable Rules and Best Practices

  • Composable functions should be small

  • Avoid storing unnecessary state

  • Keep UI functions reusable

  • Move business logic outside composables

  • Use ViewModel for application state

Understanding Recomposition

Recomposition is the process where Compose redraws parts of the UI when related state changes.

Unlike traditional Android Views, developers do not manually call methods such as setText or notify UI updates.

CounterExample.kt
kotlin
              @Composable
fun Counter() {

    var count by remember {
        mutableStateOf(0)
    }


    Button(
        onClick = {
            count++
        }
    ) {

        Text(
            text = "Count: $count"
        )
    }
}

            

State Management in Jetpack Compose

State is one of the most important concepts in Compose. The UI should always represent the current application state.

  • remember

  • rememberSaveable

  • MutableState

  • StateFlow

  • LiveData

  • ViewModel state

remember and rememberSaveable

The remember function stores values during recomposition. rememberSaveable extends this behavior by preserving state during configuration changes.

StateExample.kt
kotlin
              var username by rememberSaveable {

    mutableStateOf("")
}


TextField(

    value = username,

    onValueChange = {
        username = it
    }

)

            

Jetpack Compose Layout System

Jetpack Compose provides a flexible layout system that allows developers to build complex interfaces using simple composable functions.

The most commonly used layout components are Column, Row, and Box.

Layout Purpose
Column Places elements vertically
Row Places elements horizontally
Box Stacks elements on top of each other

Column Layout

Column arranges child components vertically. It is commonly used for screens containing multiple sections.

ColumnExample.kt
kotlin
              @Composable
fun ProfileScreen() {

    Column {

        Text(
            text = "Profile"
        )


        Text(
            text = "Android Developer"
        )
    }
}

            

Row Layout

Row places components horizontally and is commonly used for navigation bars, buttons, and lists of actions.

RowExample.kt
kotlin
              @Composable
fun ActionButtons() {

    Row {

        Button(
            onClick = {}
        ) {
            Text("Save")
        }


        Button(
            onClick = {}
        ) {
            Text("Cancel")
        }
    }
}

            

Box Layout

Box allows elements to overlap and is useful for badges, images with text overlays, and custom designs.

BoxExample.kt
kotlin
              @Composable
fun Avatar() {

    Box {

        Image(
            painter = painterResource(
                R.drawable.avatar
            ),
            contentDescription = null
        )


        Text(
            text = "Online"
        )
    }
}

            

Modifiers in Jetpack Compose

Modifiers allow developers to customize composable behavior including size, padding, alignment, click handling, and appearance.

ModifierExample.kt
kotlin
              Text(
    text = "Hello Compose",

    modifier = Modifier
        .padding(16.dp)
        .fillMaxWidth()
)

            
  • padding

  • size

  • fillMaxWidth

  • background

  • clickable

  • clip

LazyColumn and LazyRow

Lazy components are optimized lists in Jetpack Compose. They only compose visible items, making them suitable for large datasets.

UserList.kt
kotlin
              @Composable
fun UserList(
    users: List<User>
) {

    LazyColumn {

        items(users) { user ->

            Text(
                text = user.name
            )
        }
    }
}

            

Material 3 in Jetpack Compose

Material 3 is the latest design system from Google and provides modern UI components for Compose applications.

  • Buttons

  • Cards

  • Dialogs

  • Navigation bars

  • Text fields

  • Bottom sheets

Material Theme Configuration

Compose applications usually define colors, typography, and shapes inside a central theme.

Theme.kt
kotlin
              @Composable
fun AppTheme(
    content: @Composable () -> Unit
) {

    MaterialTheme {

        content()

    }
}

            

Building Reusable Components

One of the biggest advantages of Compose is the ability to create reusable UI components.

CustomButton.kt
kotlin
              @Composable
fun PrimaryButton(
    text: String,
    onClick: () -> Unit
) {

    Button(
        onClick = onClick
    ) {

        Text(text)

    }
}

            

Reusable components reduce duplication and make large applications easier to maintain.

Navigation Compose

Navigation Compose provides navigation support for applications built with Jetpack Compose.

Navigation.kt
kotlin
              NavHost(
    navController = navController,
    startDestination = "home"
) {

    composable("home") {

        HomeScreen()

    }


    composable("details") {

        DetailsScreen()

    }
}

            

Passing Data Between Screens

Modern Compose applications usually pass identifiers between screens and load data from ViewModels instead of passing large objects directly.

NavigationArgument.kt
kotlin
              navController.navigate(
    "details/user.id"
  )

            

ViewModel Integration with Compose

ViewModel is responsible for managing screen-level state and separating business logic from UI components.

UserViewModel.kt
kotlin
              class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {


    private val _state =
        MutableStateFlow(
            emptyList<User>()
        )


    val state =
        _state.asStateFlow()

}

            

Collecting StateFlow in Compose

StateFlow allows Compose screens to react automatically when application state changes.

Screen.kt
kotlin
              @Composable
fun UserScreen(
    viewModel: UserViewModel
) {

    val users by viewModel.state
        .collectAsState()


    UserList(users)

}

            

Handling Loading and Error States

Production applications should always represent different UI states such as loading, success, and failure.

UiState.kt
kotlin
              sealed interface UiState {

    data object Loading:
        UiState


    data class Success(
        val data: List<User>
    ): UiState


    data class Error(
        val message: String
    ): UiState

}

            

Forms and User Input

Compose provides modern components for building forms including TextField, Checkbox, Switch, and validation patterns.

LoginForm.kt
kotlin
              TextField(

    value = email,

    onValueChange = {
        email = it
    },

    label = {
        Text("Email")
    }

)

            

Animations in Jetpack Compose

Jetpack Compose provides powerful animation APIs that allow developers to create smooth and interactive user experiences without complex animation files.

Animations in Compose are state-driven. When a value changes, Compose can automatically animate the transition between states.

  • animate*AsState

  • AnimatedVisibility

  • AnimatedContent

  • Crossfade

  • updateTransition

animateAsState Example

AnimationExample.kt
kotlin
              @Composable
fun AnimatedBox(
    expanded: Boolean
) {

    val size by animateDpAsState(
        targetValue =
            if(expanded)
                200.dp
            else
                100.dp
    )


    Box(
        modifier = Modifier
            .size(size)
    )

}

            

The UI automatically animates between the old and new size values whenever the state changes.

Side Effects in Jetpack Compose

Composable functions can be executed multiple times because of recomposition. Side effects provide a controlled way to execute operations outside normal UI rendering.

  • LaunchedEffect

  • DisposableEffect

  • SideEffect

  • rememberCoroutineScope

LaunchedEffect Example

LaunchedEffect starts a coroutine tied to the lifecycle of a composable.

LaunchedEffectExample.kt
kotlin
              @Composable
fun UserScreen(
    viewModel: UserViewModel
) {

    LaunchedEffect(Unit) {

        viewModel.loadUsers()

    }

}

            

Jetpack Compose Performance Optimization

Although Compose simplifies UI development, poorly designed composables can still cause unnecessary recompositions and performance problems.

  • Keep composables small

  • Avoid unnecessary state changes

  • Use stable data classes

  • Use keys in LazyColumn

  • Remember expensive calculations

  • Avoid creating objects during recomposition

Using remember for Expensive Operations

The remember function prevents expensive calculations from running during every recomposition.

RememberExample.kt
kotlin
              val filteredUsers = remember(users) {

    users.filter {
        it.active
    }

}

            

Avoiding Unnecessary Recompositions

Compose uses intelligent recomposition, but developers should still design components so only required parts of the UI update.

  • Pass only required data

  • Avoid large state objects

  • Use immutable models

  • Split complex screens into smaller components

Compose Stability and Immutable Models

Stable and immutable objects allow Compose to better understand when recomposition is required.

UserModel.kt
kotlin
              @Immutable
data class User(

    val id: Long,

    val name: String,

    val email: String

)

            

Testing Jetpack Compose UI

Jetpack Compose provides testing APIs that allow developers to verify UI behavior without depending on screenshots or manual testing.

  • Finding UI elements

  • Performing user actions

  • Checking displayed text

  • Testing navigation

Compose UI Test Example

LoginScreenTest.kt
kotlin
              composeTestRule
    .onNodeWithText(
        "Login"
    )
    .assertExists()

            

Jetpack Compose vs XML Layouts

XML Layouts Jetpack Compose
Separate layout files Kotlin-based UI
Manual updates State-driven updates
More boilerplate Less code
View hierarchy Composable tree
Older approach Modern Android approach

Common Jetpack Compose Mistakes

  • Putting business logic inside composables

  • Creating huge screen composables

  • Ignoring state architecture

  • Using mutable objects everywhere

  • Overusing remember

  • Not handling loading states

  • Ignoring accessibility

Recommended Modern Compose Architecture

A scalable Jetpack Compose application usually follows a layered architecture with clear separation between UI, business logic, and data access.

Layer Responsibility
UI Layer Compose screens and components
ViewModel State management
Domain Layer Business rules and use cases
Repository Data abstraction
Data Layer API and database communication

Frequently Asked Questions

Is Jetpack Compose replacing XML completely?
Jetpack Compose is becoming the preferred approach for new Android applications, but XML layouts are still supported and used in many existing projects.
Should I learn Compose if I already know XML?
Yes. Compose represents the future direction of Android UI development and provides better integration with modern Kotlin features.
Can Jetpack Compose work with MVVM?
Yes. Compose works very well with MVVM because UI state can be exposed from ViewModels using StateFlow.
Is Compose slower than XML?
No. When implemented correctly, Compose provides excellent performance. Most issues come from inefficient state handling or unnecessary recomposition.
Can Compose be used in large enterprise applications?
Yes. Many large applications use Compose together with Clean Architecture, dependency injection, and modular design.

Conclusion

Jetpack Compose represents a major evolution in Android UI development. By using declarative programming, Kotlin-based components, modern state management, and powerful architecture patterns, developers can build faster, cleaner, and more maintainable Android applications.

We use cookies to improve your experience.