# How to understand Jetpack Compose code

> A Jetpack Compose tutorial that introduces the basics of Compose. Teaches you how to understand Jetpack Compose code, how to build basic layouts using your existing Views knowledge. Includes glimpsable cheat sheet with common Compose patterns.

Published 22 July 2022 by Alex Styl.

Building User Interfaces (UIs) using Jetpack Compose can be daunting after years of building interfaces with Android Views (XML anyone?). This article covers the basics of Jetpack Compose from an Android View background in order to speed up your learning of Compose.

> This article was featured in [Android Weekly #528](https://androidweekly.net/issues/issue-528) & [jetc.dev Newsletter Issue #125](https://jetc.dev/issues/125.html) 🎉

## From View to @Composable

In Jetpack Compose, each component that needs to be rendered on the screen can be defined as a Kotlin Unit function marked with the `@Composable` annotation like this:

```kotlin
@Composable
fun Article(title: String, description: String) {
    Card {
        Column {
            Text(title)
            Spacer(Modifier.height(10.dp))
            Text(description)
        }
    }
}
```

We call those functions _composables_. The above composable will render a Card with a title and a subtitle, with a spacing of 10 dp in between.

Every time the title and description change, the UI will be updated to reflect the updated values. This is what we call _recomposition_.

You may only call composable functions from other composable functions. Because of this, activities that use Composable to render their layouts will look like this:

```kotlin
class MyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            // use your composables here
        }
    }
}
```

`setContent {}` is an extension function of the `ComponentActivity`[^1]. Using composables in a Fragment needs a `ComposeView` like so:

```kotlin
class MyFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater?,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // use your composables here
            }
        }
    }
}
```

## Set listeners and other common attributes using Modifiers

In the View world, there are some common attributes and features found in most Views out there. Things like setting click and touch listeners, applying elevation, alpha, to name a few. For anyone creating their own custom views, there was a lot of boilerplate code to implement for your view to support such operations.

Compose introduces the concept of `Modifiers`. Modifiers provide functionality and features to composables without being tied to specific composables. Some Modifiers can be used for styling the composable (see [`background()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/package-summary#(androidx.compose.ui.Modifier).background(androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Shape)>), [`border()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/package-summary#(androidx.compose.ui.Modifier).border(androidx.compose.foundation.BorderStroke,androidx.compose.ui.graphics.Shape)>) , [`clip()`](<https://developer.android.com/reference/kotlin/androidx/compose/ui/draw/package-summary#(androidx.compose.ui.Modifier).clip(androidx.compose.ui.graphics.Shape)>) , [`shadow()`](<https://developer.android.com/reference/kotlin/androidx/compose/ui/draw/package-summary#(androidx.compose.ui.Modifier).shadow(androidx.compose.ui.unit.Dp,androidx.compose.ui.graphics.Shape,kotlin.Boolean,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color)>), [`alpha()`](<https://developer.android.com/reference/kotlin/androidx/compose/ui/draw/package-summary#(androidx.compose.ui.Modifier).alpha(kotlin.Float)>), [`animateContentSize()`](<https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#(androidx.compose.ui.Modifier).animateContentSize(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Function2)>)), others help with the placement and sizing of the composable (see [`fillMaxWidth()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.ui.Modifier).fillMaxWidth(kotlin.Float)>) , [`size()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.ui.Modifier).size(androidx.compose.ui.unit.Dp)>), [`heightIn()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.ui.Modifier).heightIn(androidx.compose.ui.unit.Dp,androidx.compose.ui.unit.Dp)>), [`padding()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.ui.Modifier).padding(androidx.compose.ui.unit.Dp,androidx.compose.ui.unit.Dp,androidx.compose.ui.unit.Dp,androidx.compose.ui.unit.Dp)>)) and others can bring functionality to the composable such as enabling click behavior or dragging (see [`clickable()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/package-summary#(androidx.compose.ui.Modifier).clickable(kotlin.Boolean,kotlin.String,androidx.compose.ui.semantics.Role,kotlin.Function0)>) , [`draggable()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.ui.Modifier).draggable(androidx.compose.foundation.gestures.DraggableState,androidx.compose.foundation.gestures.Orientation,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.Boolean,kotlin.coroutines.SuspendFunction2,kotlin.coroutines.SuspendFunction2,kotlin.Boolean)>), [`toggleable()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/selection/package-summary#(androidx.compose.ui.Modifier).toggleable(kotlin.Boolean,kotlin.Boolean,androidx.compose.ui.semantics.Role,kotlin.Function1)>), [`swipeable()`](<https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#(androidx.compose.ui.Modifier).swipeable(androidx.compose.material.SwipeableState,kotlin.collections.Map,androidx.compose.foundation.gestures.Orientation,kotlin.Boolean,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,kotlin.Function2,androidx.compose.material.ResistanceConfig,androidx.compose.ui.unit.Dp)>)).

For a full list of available modifiers [check the official documentation](https://developer.android.com/jetpack/compose/modifiers-list).

## From ViewGroups to Rows, Columns and Boxes

There are some composables whose main purpose is to help you arrange other composables on the screen. Those composables are the ViewGroups of the Compose world.

The most common ones to get started are: [`Column`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Column(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,kotlin.Function1)>), [`Row`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Row(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.ui.Alignment.Vertical,kotlin.Function1)>), [`Box`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Box(androidx.compose.ui.Modifier)>), [`LazyColumn`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#LazyColumn(androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.LazyListState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)>) and [`LazyRow`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#LazyRow(androidx.compose.ui.Modifier,androidx.compose.foundation.lazy.LazyListState,androidx.compose.foundation.layout.PaddingValues,kotlin.Boolean,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.ui.Alignment.Vertical,androidx.compose.foundation.gestures.FlingBehavior,kotlin.Boolean,kotlin.Function1)>). These composables accept the composables they need to arrange within a content lambda function. Some tend to provide a special Scope class receiver that enables access to specific Modifiers.

Instead of using a LinearLayout with a `VERTICAL` or `HORIZONTAL` orientation, use a `Column` or `Row` to arrange their composables vertically or horizontally respectively. Within their respective scope, you have access to the [`weight()`](<https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/RowScope#(androidx.compose.ui.Modifier).weight(kotlin.Float,kotlin.Boolean)>) modifier, which works the same way as a LinearLayout’s weight.

```kotlin
Row {
    Text("Main Header")
    Spacer(Modifier.weight(1f))
    Text("23 mins ago")
}
```

![Row](https://composables.com/blog-assets/static/images/row.png)

Instead of using a `FrameLayout` , use a `Box` in order to place composables on top of each other. In the Box’s Scope you have access to `align` which provides similar functionality to FrameLayout’s gravity.

```kotlin
Box {
    Image(
        painter = painterResource(R.drawable.landscape_horizontal),
        contentDescription = null
    )
    Text(
        "Preview",
        modifier = Modifier
            .padding(4.dp)
            .clip(RoundedCornerShape(14.dp))
            .background(Color.DarkGray)
            .padding(
                horizontal = 8.dp,
                vertical = 4.dp
            )
            .align(Alignment.BottomEnd),
        color = Color.White
    )
}
```

![Box](https://composables.com/blog-assets/static/images/box.png)

Working with lists is straightforward in Compose. Instead of using a `RecyclerView` with an adapter and view holders, use a `LazyColumn` or `LazyRow` to create a vertically or horizontally scrolling list respectively. The `LazyListScope` grants you access to the `item {}` function, in which you can place the items of the list without having to specify a ViewType anymore. As a bonus, there is a `stickyHeader {}` function which will provide sticky header functionality to the item.

```kotlin
val desertNames = listOf("...")
LazyColumn(Modifier.fillMaxSize()) {
    stickyHeader {
        Text(
            "Desert names",
            modifier = Modifier
                .fillMaxWidth()
                .shadow(4.dp)
                .background(Color.White)
                .padding(
                    vertical = 20.dp,
                    horizontal = 16.dp
                )
        )
    }
    desertNames.forEach { item ->
        item {
            Text(
                item,
                modifier = Modifier.padding(
                    vertical = 20.dp,
                    horizontal = 16.dp
                )
            )
        }
    }
```

![List](https://composables.com/blog-assets/static/images/list.png)

## Wiring business logic in your Screen level composables

Even though all composables are pure Kotlin functions, you will quickly realise that some are different than others. A composable might represent an element on the screen (such as a contact photo) and others might represent an entire screen. The screen level composable (the top level one) is the place where you want to be holding objects such as your ViewModels, and pass down values emitted from the ViewModel to nested composables, while reacting to events emitted from its nested composables.

Having a composable receive the values it needs to render on the screen as parameters, while bubbling up any events such as click listeners is what we call [state hoisting](https://developer.android.com/jetpack/compose/state#state-hoisting). It makes the composable _stateless_ and enables reusability and flexibility. As an added bonus, any business logic wiring ends up living in a single composable instead of being scattered around.

```kotlin
@Composable
fun MessagingScreen(viewmodel : MessagingViewModel = viewModel()){
    val typedText = viewmodel.typedText
    ReplyBar(
        text = typedText,
        onTextChange = {
            viewmodel.onTextChange(it)
        },
        onSendClick = {
            viewmodel.onSendClick()
        }
    )
}
```

## Recap

This was a quick jump into Jetpack Compose coming from an Android View background. In the Compose world, each component that needs to be rendered on the screen is marked `@Composable` called a composable.

Use Modifiers to apply common functionality to any composable such as click listeners and elevation.

Composables such as `Column` , `Row` , `Box`,`LazyColumn` and `LazyRow` are the ViewGroups of the Compose world and can be used to arrange your composables on the screen.

Keep your composables stateless and do the wiring in the screen level composable to enable reusability and flexibility.

## ⚡️ BONUS⚡ Views to Compose Cheat Sheet

Here is everything covered in a glimpsable cheat sheet:

![Compose Cheat Sheet](https://composables.com/blog-assets/static/images/compose-cheat-sheet.png)

---

## Related Videos for Jetpack Compose

Jetpack Compose Basics [(Go to lesson)](https://composables.com/lesson/basics.md)

---

This is an updated version of a blog post I first shared on medium called: [From View to @Composable](https://medium.com/@alexstyl/views-to-composables-d715b92c6055)

[^1]: ComponentActivity is part of the [androidx.activity:activity-compose](https://developer.android.com/jetpack/androidx/releases/activity) dependency.
