Mobile Development 21 min read

Master Jetpack Compose: A Beginner’s Guide to Modern Android UI Development

This article introduces Jetpack Compose, Google’s modern declarative UI toolkit for Android, explaining its origins, core concepts of declarative UI, basic usage, essential components like Text, Button, Image, layout containers, modifiers, state management, and provides practical code examples to help developers build efficient, modern Android interfaces.

BaiPing Technology
BaiPing Technology
BaiPing Technology
Master Jetpack Compose: A Beginner’s Guide to Modern Android UI Development

Compose Overview

Compose Overview
Compose Overview
Jetpack Compose

was announced at Google I/O 2019 and reached its stable 1.0.0 release in July 2021. It enables Android UI development with significantly less Kotlin code and a more intuitive API.

Why Compose Was Introduced

Jetpack Compose is Android’s modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.

Traditional XML‑based UI becomes cumbersome as screens grow more complex, requiring manual synchronization of view states. Compose solves this by using immutable, declarative UI components annotated with @Composable. When state changes, the composable function re‑executes, automatically updating the UI.

What Declarative UI Means

In imperative UI (e.g., XML + findViewById), developers manually locate views and invoke methods to change their properties. Declarative UI describes the desired UI state; the framework handles transitions.

// Imperative example
var tv: TextView = findViewById(R.id.tv)
tv.setColor(red)
// Declarative example
Text("hello ${name}", modifier = Modifier.background(color = Color.Blue))

Basic Usage

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MyApplicationTheme {
        Greeting("Android")
    }
}

@Composable : Only functions annotated with this can call other composable functions.

@Preview : Shows a preview in Android Studio without running the app.

Android Studio Preview
Android Studio Preview

setContent : Replaces the traditional setContentView by providing a composable lambda.

Core Components

Text

The Text composable is analogous to TextView. Key properties include: text: String – the displayed text. modifier: Modifier – layout and styling. color: Color – text color (e.g., Color.Blue or Color(0xFF000000)). fontSize: TextUnit – size such as 20.sp.

fontFamily, fontWeight, lineHeight, letterSpacing, textDecoration, maxLines, fontStyle, textAlign, onTextLayout, overflow

– additional styling options.

Text(
    text = "Hello BillionBottle",
    modifier = Modifier.padding(5.dp),
    color = Color.Blue,
    textAlign = TextAlign.Start,
    textDecoration = TextDecoration.LineThrough,
    fontStyle = FontStyle.Italic,
    maxLines = 1
)
Text Example
Text Example

Button

Buttons handle click events and expose several customizable properties:

onClick: () -> Unit
modifier: Modifier
enabled: Boolean
shape: Shape
border: BorderStroke?
elevation: ButtonElevation?
contentPadding: PaddingValues
colors: ButtonColors
content: @Composable () -> Unit
Button(
    onClick = {},
    modifier = Modifier.padding(12.dp),
    colors = ButtonDefaults.buttonColors(
        backgroundColor = Color.Green,
        contentColor = Color.Blue
    ),
    elevation = ButtonDefaults.elevation(
        defaultElevation = 12.dp,
        pressedElevation = 12.dp
    ),
    border = BorderStroke(width = 1.dp, color = Color.Blue)
) {
    Text(text = "BillionBottle")
}
Button Example
Button Example

Image

The Image composable replaces ImageView. Important properties: bitmap: ImageBitmap – directly provide a bitmap. contentDescription: String? – accessibility description.

modifier: Modifier
alignment: Alignment
contentScale: ContentScale
alpha: Float
colorFilter: ColorFilter
Image(
    painter = painterResource(R.drawable.xxx),
    contentDescription = ""
)
Image Example
Image Example

Surface

Surface

provides a background container for custom components.

modifier: Modifier
shape: Shape

(default RectangleShape) color: Color – background color. contentColor: Color – default text color inside the surface.

border: Border?
elevation: Dp
content: @Composable () -> Unit
Surface(modifier = Modifier.padding(4.dp), color = Color.Gray) {
    Column {
        Text(modifier = Modifier.align(Alignment.CenterHorizontally), text = "custom")
        Image(
            painter = ColorPainter(color = Color.Green),
            contentDescription = "image color"
        )
    }
}
Surface Example
Surface Example

Canvas

Canvas

allows custom drawing within a defined area. Size is set via Modifier.size or Modifier.fillMaxSize. Drawing commands such as drawLine, drawCircle, drawArc, etc., are available through DrawScope.

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height
    // Draw a blue line from top‑right to bottom‑left
    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
    // Draw a green circle at (200,1200) with radius 120
    drawCircle(color = Color.Green, center = Offset(200f, 1200f), radius = 120f)
}
Canvas Example
Canvas Example

Layout Containers

Column

Arranges children vertically, similar to LinearLayout with verticalArrangement and horizontalAlignment controls.

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    content: @Composable ColumnScope.() -> Unit
) {
    val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
    Layout(content = { ColumnScopeInstance.content() }, measurePolicy = measurePolicy, modifier = modifier)
}

Common verticalArrangement values: Arrangement.Bottom, Arrangement.Center, Arrangement.SpaceBetween, Arrangement.SpaceEvenly, Arrangement.SpaceAround.

Common horizontalAlignment values: Alignment.Start, Alignment.End, Alignment.CenterHorizontally.

@Composable
fun columnColumn() {
    Column(
        modifier = Modifier.height(100.dp).padding(5.dp),
        verticalArrangement = Arrangement.Bottom,
        horizontalAlignment = Alignment.End
    ) {
        Text("安卓")
        Text("BillionBottle")
    }
}
Column Example
Column Example

Row

Arranges children horizontally, mirroring LinearLayout with horizontalArrangement and verticalAlignment.

@Composable
inline fun Row(
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    verticalAlignment: Alignment.Vertical = Alignment.Top,
    content: @Composable RowScope.() -> Unit
) {
    val measurePolicy = rowMeasurePolicy(horizontalArrangement, verticalAlignment)
    Layout(content = { RowScopeInstance.content() }, measurePolicy = measurePolicy, modifier = modifier)
}
@Composable
fun rowShow() {
    Row(
        modifier = Modifier.width(200.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Start
    ) {
        Text("安卓")
        Text("BillionBottle")
    }
}
Row Example
Row Example

Box

Stacks children on top of each other, similar to FrameLayout. The contentAlignment parameter controls alignment of children.

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -> Unit
) {
    val measurePolicy = rememberBoxMeasurePolicy(contentAlignment, propagateMinConstraints)
    Layout(content = { BoxScopeInstance.content() }, measurePolicy = measurePolicy, modifier = modifier)
}
@Composable
fun boxLayout() {
    Box(
        contentAlignment = Alignment.BottomCenter,
        modifier = Modifier.width(100.dp).height(50.dp).padding(bottom = 10.dp)
    ) {
        Text("BillionBottle", modifier = Modifier.background(Color.Yellow))
        Text("安卓", modifier = Modifier.background(Color.Gray))
    }
}
Box Example
Box Example

Modifiers

Modifiers extend a composable’s behavior (size, background, click handling, etc.) via a chainable Modifier object. Common modifiers include background, height, size, clickable, and many others.

Column(
    modifier = Modifier
        .width(100.dp)
        .height(100.dp)
) {
    Text("BillionBottle")
    Icon(
        Icons.Filled.Favorite,
        contentDescription = "Favorite",
        modifier = Modifier
            .background(Color.Green)
            .size(ButtonDefaults.IconSize)
            .padding(2.dp)
    )
}

Changing the order of .size and .padding can noticeably affect the final layout.

State Management

Compose’s UI reacts to changes in observable state objects such as MutableState<T>. Create mutable state with mutableStateOf and remember it across recompositions using remember or rememberSaveable (which survives configuration changes).

interface MutableState<T> : State<T> {
    override var value: T
}

val state = remember { mutableStateOf("") }
var value by remember { mutableStateOf("") }
val (value, setValue) = remember { mutableStateOf("") }

Example using OutlinedTextField to edit a string and display it above the field:

@Composable
fun Greeting(name: String) {
    var info by remember { mutableStateOf(Info("")) }
    MyApplicationTheme {
        Surface(color = MaterialTheme.colors.background) {
            Column(modifier = Modifier.padding(16.dp)) {
                if (info.content.isNotEmpty()) {
                    Text(text = info.content)
                }
                OutlinedTextField(
                    value = info.content,
                    onValueChange = { info = Info(it) },
                    label = { Text("title") }
                )
            }
        }
    }
}

When the composable is destroyed due to configuration changes (e.g., rotation), remember loses its data. Use rememberSaveable with a StateSaver (e.g., parcelable, mapSaver, listSaver) to persist state.

@Parcelize
data class Info(val content: String) : Parcelable

var value by rememberSaveable { mutableStateOf(Info("")) }

Conclusion

This article provided a concise introduction to Jetpack Compose’s fundamental concepts, core UI components, layout containers, modifiers, and state handling. For deeper exploration, refer to the official documentation as the library continues to evolve with new features.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

State ManagementKotlinAndroid UIDeclarative UIJetpack Compose
BaiPing Technology
Written by

BaiPing Technology

Official account of the BaiPing app technology team. Dedicated to enhancing human productivity through technology. | DRINK FOR FUN!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.