Jetpack Compose

Advanced April 2025 Mohsen Mashkour
Jetpack Compose

Introduction

Jetpack Compose is Android's modern toolkit for building native user interfaces. Introduced by Google in 2019, it represents a paradigm shift from the traditional View-based UI system to a declarative approach, similar to React and Flutter.

This comprehensive guide will take you from the basics of Compose to building complex, production-ready UIs. Whether you're new to Android development or an experienced developer transitioning from Views, this guide will help you master Jetpack Compose.

Why Jetpack Compose?

Before diving into the code, let's understand why Compose is the future of Android UI development:

  • Declarative UI: Describe what you want, not how to create it
  • Less Code: Significantly fewer lines of code compared to XML layouts
  • Type Safety: Compile-time safety for UI components
  • Better Performance: More efficient rendering and updates
  • Kotlin-First: Built specifically for Kotlin
  • Live Preview: Real-time preview of your UI changes
  • Backward Compatibility: Works with existing View-based code

Setting Up Jetpack Compose

To use Jetpack Compose in your project, you need to add the necessary dependencies and configure your project.

Dependencies
// app/build.gradle.kts
android {
    buildFeatures {
        compose = true
    }
    
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.1"
    }
}

dependencies {
    val composeBom = platform("androidx.compose:compose-bom:2023.10.01")
    implementation(composeBom)
    
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    implementation("androidx.activity:activity-compose:1.8.2")
    
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
}
Minimum Requirements
  • Android Studio: Arctic Fox (2020.3.1) or later
  • Kotlin: 1.8.10 or later
  • Minimum SDK: API 21 (Android 5.0)
  • Target SDK: API 34 (Android 14)

Your First Compose App

Let's start with a simple "Hello World" example to understand the basic structure.

Basic Composable Function
@Composable
fun HelloWorld() {
    Text(text = "Hello, Compose!")
}

@Composable
fun MyApp() {
    MaterialTheme {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            HelloWorld()
        }
    }
}
Activity Setup
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApp()
        }
    }
}
Understanding the Structure
  • @Composable: Annotation that marks a function as a composable
  • MaterialTheme: Provides design tokens and theming
  • Surface: Material Design container with elevation
  • Modifier: Chain of modifications for layout and behavior

Basic Composables

Compose provides a rich set of built-in composables for common UI elements.

Text Composable
@Composable
fun TextExamples() {
    Column {
        // Basic text
        Text("Simple text")
        
        // Styled text
        Text(
            text = "Styled text",
            color = Color.Blue,
            fontSize = 18.sp,
            fontWeight = FontWeight.Bold
        )
        
        // Text with modifier
        Text(
            text = "Text with background",
            modifier = Modifier
                .background(Color.Yellow)
                .padding(8.dp)
        )
        
        // Annotated text
        Text(
            text = buildAnnotatedString {
                withStyle(SpanStyle(color = Color.Red)) {
                    append("Red text ")
                }
                withStyle(SpanStyle(color = Color.Blue)) {
                    append("Blue text")
                }
            }
        )
    }
}
Button Composable
@Composable
fun ButtonExamples() {
    Column(spacing = 8.dp) {
        // Basic button
        Button(onClick = { /* Handle click */ }) {
            Text("Click me")
        }
        
        // Outlined button
        OutlinedButton(onClick = { /* Handle click */ }) {
            Text("Outlined Button")
        }
        
        // Text button
        TextButton(onClick = { /* Handle click */ }) {
            Text("Text Button")
        }
        
        // Button with icon
        Button(
            onClick = { /* Handle click */ },
            modifier = Modifier.fillMaxWidth()
        ) {
            Icon(
                imageVector = Icons.Default.Add,
                contentDescription = "Add icon"
            )
            Spacer(modifier = Modifier.width(8.dp))
            Text("Add Item")
        }
    }
}
Image Composable
@Composable
fun ImageExamples() {
    Column {
        // Basic image
        Image(
            painter = painterResource(id = R.drawable.my_image),
            contentDescription = "My image"
        )
        
        // Image with modifier
        Image(
            painter = painterResource(id = R.drawable.my_image),
            contentDescription = "My image",
            modifier = Modifier
                .size(100.dp)
                .clip(CircleShape)
        )
        
        // Async image with Coil
        AsyncImage(
            model = "https://example.com/image.jpg",
            contentDescription = "Remote image",
            modifier = Modifier.size(200.dp),
            contentScale = ContentScale.Crop
        )
    }
}

Layout Composables

Compose provides several layout composables to arrange your UI elements.

Column and Row
@Composable
fun LayoutExamples() {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text("Column - Vertical Layout")
        
        Spacer(modifier = Modifier.height(16.dp))
        
        Row(
            horizontalArrangement = Arrangement.SpaceEvenly,
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Row - Horizontal Layout")
            Text("Item 2")
            Text("Item 3")
        }
        
        Spacer(modifier = Modifier.height(16.dp))
        
        // Nested layouts
        Column {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Text("Left")
                Text("Right")
            }
            
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceEvenly
            ) {
                Text("Evenly")
                Text("Spaced")
                Text("Items")
            }
        }
    }
}
Box Layout
@Composable
fun BoxExamples() {
    Box(
        modifier = Modifier
            .size(200.dp)
            .background(Color.LightGray)
    ) {
        // Background content
        Text(
            text = "Background",
            modifier = Modifier.align(Alignment.Center)
        )
        
        // Overlay content
        Text(
            text = "Overlay",
            modifier = Modifier
                .align(Alignment.TopStart)
                .background(Color.Red.copy(alpha = 0.7f))
                .padding(4.dp)
        )
        
        // Another overlay
        Button(
            onClick = { /* Handle click */ },
            modifier = Modifier.align(Alignment.BottomEnd)
        ) {
            Text("Button")
        }
    }
}
LazyColumn and LazyRow
@Composable
fun LazyListExamples() {
    val items = List(100) { "Item $it" }
    
    LazyColumn {
        items(items) { item ->
            Text(
                text = item,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp)
                    .background(Color.White)
            )
        }
    }
}

@Composable
fun LazyRowExample() {
    val items = List(20) { "Item $it" }
    
    LazyRow(
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        contentPadding = PaddingValues(horizontal = 16.dp)
    ) {
        items(items) { item ->
            Card(
                modifier = Modifier.width(120.dp)
            ) {
                Text(
                    text = item,
                    modifier = Modifier.padding(8.dp)
                )
            }
        }
    }
}

State Management

State management is crucial in Compose. Understanding how to handle state properly will make your apps more responsive and maintainable.

Remember and MutableState
@Composable
fun CounterExample() {
    var count by remember { mutableStateOf(0) }
    
    Column(
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Count: $count")
        
        Button(onClick = { count++ }) {
            Text("Increment")
        }
        
        Button(onClick = { count-- }) {
            Text("Decrement")
        }
    }
}
State Hoisting
@Composable
fun StateHoistingExample() {
    var text by remember { mutableStateOf("") }
    
    Column {
        TextField(
            value = text,
            onValueChange = { text = it },
            label = { Text("Enter text") }
        )
        
        Text("You entered: $text")
        
        // Child component receives state as parameter
        ChildComponent(
            text = text,
            onTextChange = { text = it }
        )
    }
}

@Composable
fun ChildComponent(
    text: String,
    onTextChange: (String) -> Unit
) {
    TextField(
        value = text,
        onValueChange = onTextChange,
        label = { Text("Child input") }
    )
}
Derived State
@Composable
fun DerivedStateExample() {
    var firstName by remember { mutableStateOf("") }
    var lastName by remember { mutableStateOf("") }
    
    // Derived state - computed from other state
    val fullName by remember(firstName, lastName) {
        derivedStateOf {
            if (firstName.isBlank() && lastName.isBlank()) {
                "No name entered"
            } else {
                "$firstName $lastName".trim()
            }
        }
    }
    
    Column {
        TextField(
            value = firstName,
            onValueChange = { firstName = it },
            label = { Text("First Name") }
        )
        
        TextField(
            value = lastName,
            onValueChange = { lastName = it },
            label = { Text("Last Name") }
        )
        
        Text("Full Name: $fullName")
    }
}

Material Design 3

Material Design 3 is the latest design system from Google, and Compose makes it easy to implement.

Material 3 Theme
@Composable
fun Material3Example() {
    MaterialTheme(
        colorScheme = lightColorScheme(
            primary = Color(0xFF6750A4),
            secondary = Color(0xFF625B71),
            tertiary = Color(0xFF7D5260)
        ),
        typography = Typography(
            bodyLarge = TextStyle(
                fontFamily = FontFamily.Default,
                fontWeight = FontWeight.Normal,
                fontSize = 16.sp,
                lineHeight = 24.sp,
                letterSpacing = 0.5.sp
            )
        )
    ) {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            Column(
                modifier = Modifier.padding(16.dp)
            ) {
                Text(
                    text = "Material 3 App",
                    style = MaterialTheme.typography.headlineMedium
                )
                
                Spacer(modifier = Modifier.height(16.dp))
                
                Button(
                    onClick = { /* Handle click */ },
                    colors = ButtonDefaults.buttonColors(
                        containerColor = MaterialTheme.colorScheme.primary
                    )
                ) {
                    Text("Primary Button")
                }
                
                Spacer(modifier = Modifier.height(8.dp))
                
                OutlinedButton(
                    onClick = { /* Handle click */ }
                ) {
                    Text("Secondary Button")
                }
            }
        }
    }
}
Cards and Elevation
@Composable
fun CardExamples() {
    Column(spacing = 16.dp) {
        // Elevated card
        ElevatedCard(
            onClick = { /* Handle click */ },
            modifier = Modifier.fillMaxWidth()
        ) {
            Column(modifier = Modifier.padding(16.dp)) {
                Text(
                    text = "Elevated Card",
                    style = MaterialTheme.typography.headlineSmall
                )
                Text(
                    text = "This card has elevation and is clickable",
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
        
        // Filled card
        FilledCard(
            modifier = Modifier.fillMaxWidth()
        ) {
            Column(modifier = Modifier.padding(16.dp)) {
                Text(
                    text = "Filled Card",
                    style = MaterialTheme.typography.headlineSmall
                )
                Text(
                    text = "This card has a filled background",
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
        
        // Outlined card
        OutlinedCard(
            modifier = Modifier.fillMaxWidth()
        ) {
            Column(modifier = Modifier.padding(16.dp)) {
                Text(
                    text = "Outlined Card",
                    style = MaterialTheme.typography.headlineSmall
                )
                Text(
                    text = "This card has an outline",
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
    }
}

Navigation

Navigation in Compose is handled by the Navigation Compose library, which provides a type-safe way to navigate between screens.

Basic Navigation Setup
// Add to dependencies
implementation("androidx.navigation:navigation-compose:2.7.5")

@Composable
fun NavigationExample() {
    val navController = rememberNavController()
    
    NavHost(
        navController = navController,
        startDestination = "home"
    ) {
        composable("home") {
            HomeScreen(navController)
        }
        composable("profile") {
            ProfileScreen(navController)
        }
        composable("settings") {
            SettingsScreen(navController)
        }
    }
}

@Composable
fun HomeScreen(navController: NavController) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Home Screen",
            style = MaterialTheme.typography.headlineMedium
        )
        
        Spacer(modifier = Modifier.height(16.dp))
        
        Button(
            onClick = { navController.navigate("profile") }
        ) {
            Text("Go to Profile")
        }
        
        Spacer(modifier = Modifier.height(8.dp))
        
        Button(
            onClick = { navController.navigate("settings") }
        ) {
            Text("Go to Settings")
        }
    }
}
Navigation with Arguments
@Composable
fun NavigationWithArgs() {
    val navController = rememberNavController()
    
    NavHost(
        navController = navController,
        startDestination = "home"
    ) {
        composable("home") {
            HomeScreen(navController)
        }
        composable(
            "detail/{userId}",
            arguments = listOf(
                navArgument("userId") { type = NavType.StringType }
            )
        ) { backStackEntry ->
            val userId = backStackEntry.arguments?.getString("userId")
            DetailScreen(userId = userId, navController = navController)
        }
    }
}

@Composable
fun HomeScreen(navController: NavController) {
    Column(
        modifier = Modifier.padding(16.dp)
    ) {
        Text("Home Screen")
        
        Button(
            onClick = { navController.navigate("detail/user123") }
        ) {
            Text("View User Details")
        }
    }
}

@Composable
fun DetailScreen(userId: String?, navController: NavController) {
    Column(
        modifier = Modifier.padding(16.dp)
    ) {
        Text("User ID: $userId")
        
        Button(
            onClick = { navController.popBackStack() }
        ) {
            Text("Go Back")
        }
    }
}

Practical Examples

Let's build some real-world examples to demonstrate Compose concepts in action.

Todo List App
data class Todo(
    val id: Int,
    val text: String,
    val isCompleted: Boolean = false
)

@Composable
fun TodoApp() {
    var todos by remember { mutableStateOf(listOf()) }
    var newTodoText by remember { mutableStateOf("") }
    
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        // Add new todo
        Row(
            modifier = Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically
        ) {
            TextField(
                value = newTodoText,
                onValueChange = { newTodoText = it },
                modifier = Modifier.weight(1f),
                placeholder = { Text("Add a new todo") }
            )
            
            Spacer(modifier = Modifier.width(8.dp))
            
            Button(
                onClick = {
                    if (newTodoText.isNotBlank()) {
                        todos = todos + Todo(
                            id = todos.size,
                            text = newTodoText
                        )
                        newTodoText = ""
                    }
                }
            ) {
                Text("Add")
            }
        }
        
        Spacer(modifier = Modifier.height(16.dp))
        
        // Todo list
        LazyColumn {
            items(todos) { todo ->
                TodoItem(
                    todo = todo,
                    onToggle = { 
                        todos = todos.map { 
                            if (it.id == todo.id) it.copy(isCompleted = !it.isCompleted)
                            else it
                        }
                    },
                    onDelete = {
                        todos = todos.filter { it.id != todo.id }
                    }
                )
            }
        }
    }
}

@Composable
fun TodoItem(
    todo: Todo,
    onToggle: () -> Unit,
    onDelete: () -> Unit
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Checkbox(
            checked = todo.isCompleted,
            onCheckedChange = { onToggle() }
        )
        
        Text(
            text = todo.text,
            modifier = Modifier.weight(1f),
            textDecoration = if (todo.isCompleted) {
                TextDecoration.LineThrough
            } else {
                TextDecoration.None
            }
        )
        
        IconButton(onClick = onDelete) {
            Icon(
                imageVector = Icons.Default.Delete,
                contentDescription = "Delete todo"
            )
        }
    }
}
Weather App UI
data class WeatherInfo(
    val temperature: Int,
    val condition: String,
    val humidity: Int,
    val windSpeed: Int
)

@Composable
fun WeatherApp() {
    var weatherInfo by remember { 
        mutableStateOf(WeatherInfo(22, "Sunny", 65, 12))
    }
    
    MaterialTheme {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(16.dp)
            ) {
                // Header
                Text(
                    text = "Weather App",
                    style = MaterialTheme.typography.headlineLarge,
                    modifier = Modifier.padding(bottom = 16.dp)
                )
                
                // Weather card
                ElevatedCard(
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Column(
                        modifier = Modifier.padding(16.dp)
                    ) {
                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.SpaceBetween,
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Column {
                                Text(
                                    text = "${weatherInfo.temperature}°C",
                                    style = MaterialTheme.typography.headlineLarge
                                )
                                Text(
                                    text = weatherInfo.condition,
                                    style = MaterialTheme.typography.bodyLarge
                                )
                            }
                            
                            Icon(
                                imageVector = Icons.Default.WbSunny,
                                contentDescription = "Weather icon",
                                modifier = Modifier.size(48.dp),
                                tint = Color.Yellow
                            )
                        }
                        
                        Spacer(modifier = Modifier.height(16.dp))
                        
                        // Weather details
                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.SpaceEvenly
                        ) {
                            WeatherDetail(
                                icon = Icons.Default.Opacity,
                                label = "Humidity",
                                value = "${weatherInfo.humidity}%"
                            )
                            
                            WeatherDetail(
                                icon = Icons.Default.Air,
                                label = "Wind",
                                value = "${weatherInfo.windSpeed} km/h"
                            )
                        }
                    }
                }
                
                Spacer(modifier = Modifier.height(16.dp))
                
                // Refresh button
                Button(
                    onClick = {
                        // Simulate weather update
                        weatherInfo = weatherInfo.copy(
                            temperature = (15..30).random(),
                            humidity = (40..80).random(),
                            windSpeed = (5..20).random()
                        )
                    },
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Icon(
                        imageVector = Icons.Default.Refresh,
                        contentDescription = "Refresh"
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Text("Refresh Weather")
                }
            }
        }
    }
}

@Composable
fun WeatherDetail(
    icon: ImageVector,
    label: String,
    value: String
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Icon(
            imageVector = icon,
            contentDescription = label,
            modifier = Modifier.size(24.dp)
        )
        Text(
            text = label,
            style = MaterialTheme.typography.bodySmall
        )
        Text(
            text = value,
            style = MaterialTheme.typography.bodyMedium,
            fontWeight = FontWeight.Bold
        )
    }
}

Performance Optimization

Compose is designed to be performant, but there are several techniques you can use to optimize your apps.

Remember and RememberSaveable
@Composable
fun PerformanceExample() {
    // Use remember for expensive computations
    val expensiveValue by remember {
        derivedStateOf {
            // Expensive computation
            (1..1000).sum()
        }
    }
    
    // Use rememberSaveable for state that survives configuration changes
    var userInput by rememberSaveable { mutableStateOf("") }
    
    Column {
        Text("Expensive value: $expensiveValue")
        TextField(
            value = userInput,
            onValueChange = { userInput = it }
        )
    }
}
Lazy Loading
@Composable
fun LazyLoadingExample() {
    val items = List(1000) { "Item $it" }
    
    LazyColumn {
        items(
            items = items,
            key = { it } // Stable keys for better performance
        ) { item ->
            Text(
                text = item,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp)
            )
        }
    }
}
Composition Local
// Define composition local
val LocalTheme = compositionLocalOf { "light" }

@Composable
fun ThemeProvider() {
    CompositionLocalProvider(LocalTheme provides "dark") {
        ChildComponent()
    }
}

@Composable
fun ChildComponent() {
    val theme = LocalTheme.current
    Text("Current theme: $theme")
}

Testing

Testing Compose UI is straightforward with the Compose testing library.

Basic UI Tests
// Add to dependencies
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.5.4")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.5.4")

@Composable
fun TestableComponent() {
    var count by remember { mutableStateOf(0) }
    
    Column {
        Text("Count: $count")
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}

@Test
fun testIncrementButton() {
    composeTestRule.setContent {
        TestableComponent()
    }
    
    // Find text and verify initial state
    composeTestRule.onNodeWithText("Count: 0").assertExists()
    
    // Click button
    composeTestRule.onNodeWithText("Increment").performClick()
    
    // Verify state changed
    composeTestRule.onNodeWithText("Count: 1").assertExists()
}
Semantics for Testing
@Composable
fun TestableButton() {
    Button(
        onClick = { /* Handle click */ },
        modifier = Modifier.testTag("increment_button")
    ) {
        Text("Increment")
    }
}

@Test
fun testButtonWithTag() {
    composeTestRule.setContent {
        TestableButton()
    }
    
    // Find by test tag
    composeTestRule.onNodeWithTag("increment_button").performClick()
}

Best Practices

Composable Design
  • Keep composables small: Break large composables into smaller, reusable ones
  • Use meaningful names: Name your composables descriptively
  • Extract parameters: Make composables configurable through parameters
  • Follow single responsibility: Each composable should have one clear purpose
State Management
  • Hoist state up: Keep state as close to where it's used as possible
  • Use remember appropriately: Remember expensive computations and state
  • Prefer immutable state: Use data classes for complex state
  • Use derived state: Compute values from other state when possible
Performance
  • Use LazyColumn/LazyRow: For long lists
  • Provide stable keys: For list items and animations
  • Minimize recompositions: Use remember and derived state
  • Profile your app: Use Android Studio's profiler

Common Pitfalls

Avoiding Common Mistakes
  • Don't call composables conditionally: Always call them in the same order
  • Don't forget remember: Use remember for state and expensive computations
  • Don't ignore side effects: Use LaunchedEffect for one-time effects
  • Don't over-compose: Keep composables focused and not too deep
Debugging Tips
  • Use Compose Preview: Test your UI in isolation
  • Enable recomposition counting: In debug builds
  • Use Layout Inspector: To debug layout issues
  • Check Compose Compiler: For compilation errors

Practice Exercises

Try these exercises to reinforce your Compose knowledge:

Exercise 1: Counter App
// Create a counter app with:
// - Display current count
// - Increment and decrement buttons
// - Reset button
// - Color-coded display (red for negative, green for positive)
Exercise 2: Form Validation
// Create a form with:
// - Email input with validation
// - Password input with strength indicator
// - Submit button (enabled only when valid)
// - Error messages for invalid inputs
Exercise 3: Shopping Cart
// Create a shopping cart with:
// - List of products
// - Add/remove items
// - Quantity controls
// - Total price calculation
// - Checkout button

Next Steps

Now that you have a solid foundation in Jetpack Compose, explore these advanced topics:

  • Animations: Learn animate*AsState and transition APIs
  • Custom Composables: Create reusable UI components
  • Integration with Views: Mix Compose with existing View code
  • Advanced State: Explore StateFlow and ViewModel integration
  • Material 3 Theming: Create custom design systems
  • Performance Profiling: Optimize your Compose apps

Resources

Summary

Jetpack Compose represents the future of Android UI development. Its declarative approach, type safety, and powerful features make it an excellent choice for building modern Android applications.

You've learned the fundamentals of Compose, from basic composables to state management, navigation, and performance optimization. The key to mastering Compose is practice - build real applications, experiment with different patterns, and stay updated with the latest features and best practices.

Remember that Compose is still evolving, so keep learning and exploring new capabilities. The Android developer community is actively contributing to its growth, and there are always new techniques and patterns to discover.