Compose Fundamentals: Core Concepts of Declarative UI

Compose Fundamentals: Core Concepts of Declarative UI 

Jetpack Compose (v1.9 stable, Aug 2025) revolutionizes Android UI with declarative paradigms. No XML, pure Kotlin functions. Focus: State drives UI. check

Declarative vs Imperative UI code

Direct: Use declarative (Compose) – auto-updates UI on state change. Imperative (Views) manual, error-prone.

  • Imperative: Commands like "set text" – forgets state on rotation.
  • Declarative: "Show text if state=X" – framework handles all. bolt
Aspect Imperative (Views) Declarative (Compose)
Code Manual findViewById + setText StateFlow triggers recompose
State Bundle saves needed remember { } auto-saves
Perf Full redraws Smart diffs
  • Imperative Pros check: Legacy support
  • Imperative Cons close: Boilerplate, leaks
  • Declarative Pros check: Reactive, testable
  • Declarative Cons close: Learn Kotlin DSL

Full Toggle Example: See Practice section. smartphone

Compose Setup and First Composable settings

Direct: Add to build.gradle, enable compiler. Run first "Hello" composable.

build.gradle (Module: app) – Full

plugins {
    id 'com.android.application' // Standard Android plugin
    id 'org.jetbrains.kotlin.android' // Kotlin support
    id 'kotlin-kapt' // Annotation processing if needed
}
android {
    namespace 'com.example.myapp' // App namespace
    compileSdk 35 // Target Android 15
    defaultConfig {
        applicationId "com.example.myapp" // Unique app ID
        minSdk 24 // Minimum API level (Android 7.0+)
        targetSdk 35 // Target API level
        versionCode 1
        versionName "1.0"
    }
    buildFeatures {
        compose true // Enable Jetpack Compose
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.5.15' // Compose compiler version for 1.9
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}
dependencies {
    implementation 'androidx.core:core-ktx:1.13.1' // Core KTX
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.4' // Lifecycle runtime
    implementation 'androidx.activity:activity-compose:1.9.1' // Activity integration for Compose
    implementation platform('androidx.compose:compose-bom:2024.10.00') // Compose BOM for versions
    implementation 'androidx.compose.ui:ui' // Compose UI core
    implementation 'androidx.compose.ui:ui-graphics' // Graphics utilities
    implementation 'androidx.compose.ui:ui-tooling-preview' // Preview support
    implementation 'androidx.compose.material3:material3' // Material Design 3 components
    debugImplementation 'androidx.compose.ui:ui-tooling' // UI tooling for debug
    debugImplementation 'androidx.compose.ui:ui-test-manifest' // Test manifest
}

First Composable – Full MainActivity.kt

package com.example.myapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent // Sets Compose content in Activity
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme // Material 3 theme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable // Marks composable functions
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview // For previewing in Android Studio
import com.example.myapp.ui.theme.MyAppTheme // Custom theme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(), // Fill entire screen
                    color = MaterialTheme.colorScheme.background // Background color from theme
                ) {
                    Greeting("Android") // Call the Greeting composable
                }
            }
        }
    }
}

// Simple Greeting composable function
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!", // Interpolated text
        modifier = modifier // Apply modifier if provided
    )
}

// Preview function for Android Studio
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyAppTheme {
        Greeting("Android")
    }
}
  • Sync Gradle → Run → See "Hello Android!" bolt

Composable Functions and @Composable functions

Direct: @Composable marks UI builders. Parameters flow down, emit UI up.

  • Rules: No side-effects outside; use remember for state.
  • Best: Pure functions = reusable, testable. refresh

Full Nested Example

@Composable
fun App() {
    Column { // Vertical layout container
        Greeting("World") // Calls child composable
        Button(onClick = { /* Action */ }) { // Button with click handler
            Text("Click") // Button label
        }
    }
}

// Greeting composable
@Composable
fun Greeting(name: String) {
    Text("Hello $name!") // Emits Text UI node
}

Compose Compiler and Runtime build

Direct: Compiler (Kotlin 1.9+) slots params to stable IDs. Runtime composes tree.

  • Compiler: Transforms @Composable to set-stable calls.
  • Runtime: Builds/rebuilds UI tree via CompositionLocal. settings

Version: kotlinCompilerExtensionVersion '1.5.15' (Compose 1.9). No manual IDs needed. check

Preview and Tooling Support preview

Direct: @Preview for live Android Studio views. No run needed.

  • Tooling: Layout Inspector, Theme Adapter. Debug with ui-tooling.
  • Best: @Preview(params) for variants. visibility

Full Preview Example

import android.content.res.Configuration // For UI mode

@Preview(name = "Light", showBackground = true) // Light theme preview
@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES) // Dark theme preview
@Composable
fun GreetingPreview() {
    MyAppTheme {
        Greeting("Preview")
    }
}

Composition and Recomposition refresh

Direct: Composition builds initial tree. Recomposition updates changed parts only.

  • Skip: Unchanged composables (stability checks).
  • Best: Use derivedStateOf for derived values. refresh

Full Recompose Example

import androidx.compose.runtime.mutableIntStateOf // For mutable state
import androidx.compose.runtime.remember // Remember state across recompositions

@Composable
fun Counter() {
    var count by remember { mutableIntStateOf(0) } // Initial state: 0, triggers recompose on change
    Text("Count: $count") // Displays current count
    Button(onClick = { count++ }) { // Increment on click
        Text("Increment") // Button label
    }
    // Only Text + Button recompose on count change; efficient!
}

Practice: Hello Compose App play_arrow

Direct: Build toggle app. Full code below. Run & tweak.

Full MainActivity.kt

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.* // Layout modifiers
import androidx.compose.material3.* // Material 3 components
import androidx.compose.runtime.* // State and remember
import androidx.compose.ui.Alignment // Alignment utilities
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp // Density-independent pixels
import androidx.compose.ui.unit.sp // Scalable pixels
import com.example.helloapp.ui.theme.HelloAppTheme // Custom theme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            HelloAppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    HelloScreen() // Main screen composable
                }
            }
        }
    }
}

// Main screen with toggle functionality
@Composable
fun HelloScreen() {
    var isToggled by rememberSaveState { false } // Persist state across config changes
    Column( // Vertical stack
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp), // Padding around edges
        verticalArrangement = Arrangement.Center, // Center vertically
        horizontalAlignment = Alignment.CenterHorizontally // Center horizontally
    ) {
        Text( // Dynamic text based on state
            text = if (isToggled) "Toggled!" else "Hello Compose!",
            fontSize = 24.sp, // Text size
            modifier = Modifier.padding(bottom = 16.dp) // Bottom padding
        )
        Button(onClick = { isToggled = !isToggled }) { // Toggle state on click
            Text("Toggle") // Button label
        }
    }
}
  • Steps: Copy to project → Sync → Run → Rotate screen (state saves). celebration

Post a Comment

Previous Post Next Post