SoFunction
Updated on 2025-04-10

Jetpack Compose Step-by-step guide tutorial detailed explanation

Preface

In this chapter, we will learn about Jetpack Compose, a modern toolkit for building native UIs.

With this complete tutorial, we will learn how to use Text, TextField, Preview, Column, Row, Button, Card, AlertDialog, MaterialDesign elements, and more. So, without further ado, let's start creating a Jetpack Compose project. Therefore, this chapter is about learning Jetpack Compose for Android with examples.

Notice:To use Jetpack Compose, you need to have the latest Canary version of Android Studio 4.2. Therefore, you can go to the Android Studio preview page and download the latest Canary version and create an Empty Compose Activity.

Compositionable functions

In Jetpack Compose, combinable functions are used to programmatically define all UIs of an application. Therefore, you do not need to use any XML files for the layout of your application. All you need to do to make a composable function is to use the comments of the @Composable function name. The basic syntax of a composable function is:

@Composable
fun AnyUiComponent() {
    // Code for UI element
}

Now you know what a composable function is and how to create a composable function using the @Composable annotation. Let's continue to look at the Text example.

Show simple text

In this part of this chapter, we will learn how to use compose to display simple text.

To display text, we use Text Composable and pass the string to display there. For example,

@Composable
fun SimpleText(displayText: String) {
    Text(text = displayText)
}

Here, SimpleText is a composable function, inside which we are using Text and passing displayText to it.

You can now call this SimpleText function from the block of the active method. setContentonCreate

class SimpleTextActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        (savedInstanceState)
        setContent {
            Column(
                modifier = (),
                verticalArrangement = ,
                horizontalAlignment = ,
            ) {
                SimpleText(getString("I am learning Compose"))
            }
        }
    }
}

Here we use a Column for vertically displaying something, and we are calling the SimpleTextComposable function.

Apply styles to text

We can apply various styles to text, such as increasing font size, changing color, etc.

So let's create a function called SetTextStyling:

@Composable
fun SetTextStyling(displayText: String, style: TextStyle? = null, maxLines: Int? = null) {
    Text(
        text = displayText,
        modifier = (),
        style = style ?: ,
        overflow = ,
        maxLines = maxLines ?: Int.MAX_VALUE
    )
}

In the above function, the parameters are displayText, that is, the text to be displayed, style, that is, the style to be placed on Text, and maxLines is the maximum number of lines allowed by the text. If the text exceeds the maximum line, an ellipsis (...) will be displayed.

We will call this function by passing these parameters. Let's take a look at some of them:

  • Set the font size:
style = TextStyle(
    fontSize = 
)
  • To set the font thickness, pass text-style as:
fontWeight = 

Similarly, you can change the font size, text color, font series, underline text, and more. You can see all of this in our open source projects.

Use TextField for input

Just like EditText, in Compose we can use TextField and BaseTextField. BaseTextField is still in the trial stage and can be deleted or added permanently at any time. So to use BaseTextField you need to add the @ExperimentalFoundationApi annotation.

Here is a simple example BaseTextField:

@ExperimentalFoundationApi
@Composable
fun SimpleTextFieldComponent() {
    Surface(color = , modifier = ()) {
        var text by remember { mutableStateOf(TextFieldValue("Enter text here")) }
        BaseTextField(
            value = text,
            modifier = ().fillMaxWidth(),
            onValueChange = {
                text = it
            }
        )
    }
}

In the above function we have a BaseTextFieldinside a Surface. We have a callback called onValueChange. This callback is called when some change occurs in the input of , BaseTextField and the updated text will appear as a parameter to the callback.

Here is an example BaseTextField. Material Design also provides EditText with a Composable, namely TextField. A simple implementation of TextField is as follows:

@Composable
fun SimpleMaterialTextFieldComponent() {
    var text by savedInstanceState { "" }
    TextField(
        value = text,
        modifier = ().fillMaxWidth(),
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

TextField behaves similarly to BaseTextField. Here TextField, you have one more thing, namely label. A label is a text that is displayed in TextField when there is no text in a TextField.

We can customize this BaseTextField and TextField by passing various parameters to it. For example,

  • Show only the numeric keypad:
var text by remember { mutableStateOf(TextFieldValue("0123")) }
BaseTextField(value = text,
    keyboardType = ,
    onValueChange = {
        text = it
    }
)
  • Take the password as input:
keyboardType = ,
visualTransformation = PasswordVisualTransformation()
  • Add placeholder in TextField (displayed when TextField is empty and has focus)
placeholder = { Text("MindOrks") }

Similarly, we can add icons, display error messages in TextFiled, and set errorColor, backgroundColor, intractionState, activeColor, inactiveColor, etc. You can find these in our open source project.

You can try these and view the output in Android Studio itself. Yes, you heard it right. You can preview any UI element in Android Studio itself. Let's see how.

Preview in Android Studio

Android Studio provides a great feature to preview your UI components in the studio itself, and it's very dynamic. So whenever you want to test some UI components, you can simply preview it in Android Studio by making a Composable function and using the @Preview annotation.

Here are the same examples:

// This is a Composable function to display a Text
@Composable
fun SimpleText(displayText: String) {
    Text(text = displayText)
}
@Preview
@Composable
fun SimpleTextPreview() {
    SimpleText("Hi I am learning Compose")
}

Now, in the Preview tab (to the right of Studio), you can see a preview of the above Composable function.

You can use any number of previews of different widths and heights. If you click on any UI element in the preview, Android Studio takes you to the line that created the UI.

Additionally, you can use the parameters to put some names into the preview name. To name the preview, you need to add the following code:

@Preview(name = "Named Preview")

By default, the preview name is the name of the function.

Note: You cannot pass any arguments to the Preview Composable function.

Preview parameters

In the previous section, we learned how to use the preview feature of Android Studio. Now, when we make any Android application, in most cases the data comes from the server, we populate this data into our application. So the next question is how to preview the data from the server's UI, i.e., its data is now unavailable. For these cases, we can use the @PreviewParameter annotation.

The main idea is to make a virtual data and pass that virtual data to a preview composable function. Since you can't pass any parameters to the Preview Composable Function, in order to achieve this, you need to pass the parameters using the @PreviewParamter annotation.

So, let's first create a virtual data class.

Create a data class named and add the following code to it:

data class Blog(
    val name: String,
    val author: String
)

Now, create a class named DummyBlogProvider, which implements an interface named PreviewParameterProvider:

class DummyBlogProvider : PreviewParameterProvider<Blog> {
    override val values =
        sequenceOf(Blog("Learning Compose", "MindOrks"), Blog("Learning Android", "MindOrks"))
    override val count: Int = ()
}

Now that we have finished the virtual data, we can use this virtual data in the preview. Here are the same examples:

@Preview
@Composable
fun BlogInfo(@PreviewParameter(DummyBlogProvider::class) blog: Blog) {
    SimpleTextComponent("${} by ${}")
}

You can use virtual data to view previews of the UI.

Column

AColumn is a composable layout for placing all its children vertically one after another. It is similar to LinearLayout with a vertical orientation.

Here is an example of the column:

@Composable
fun SimpleColumnComponent() {
    Column(modifier = ()) {
        Text(text = "Hello! I am Text 1", color = )
        Text(text = "Hello! I am Text 2", color = )
    }
}

Scrollable Column

When you use simpleColumn, you can only use the height of your phone's screen. However, if your content is out of the screen, you can use ScrollableColumn. ScrollableColumn is like a ScrollView.

Here are the same examples:

@Composable
fun ScrollableColumnComponent(blogList: List<Blog>) {
    ScrollableColumn {
        val context = 
        Column {
            for (blog in blogList) {
                Card(
                    shape = RoundedCornerShape(),
                    modifier = ().padding().clickable(onClick = {
                        (context, "Author: ${}", Toast.LENGTH_SHORT).show()
                    }),
                    backgroundColor = Color(())
                ) {
                    Text(
                        , style = TextStyle(
                            fontSize = ,
                            textAlign = 
                        ), modifier = ()
                    )
                }
            }
        }
    }
}

Lazy Column

ScrollableColumn loads all its elements at the beginning. For example, if you have 50 elements and the screen can only show 10 elements at any point in time, and you need to scroll to see another element, then if you are using a ScrollableColumn, all elements will be loaded initially.

However, if you are using LazyColumnFor, it will only load those elements currently visible on the mobile screen. It behaves a bit like a RecyclerView.

Here are the same examples:

@Composable
fun LazyColumnScrollableComponent(blogList: List&lt;Blog&gt;) {
    LazyColumnFor(items = blogList, modifier = ()) { blog -&gt;
        val context = 
        Card(
            shape = RoundedCornerShape(),
            modifier = ().padding().clickable(onClick = {
                (context, "Author: ${}", Toast.LENGTH_SHORT).show()
            }),
            backgroundColor = Color(())
        ) {
            Text(
                , style = TextStyle(
                    fontSize = ,
                    textAlign = 
                ), modifier = ()
            )
        }
    }
}

Now, if you want to display content horizontally, you can use Row, ScrollableRow, or Lazy Row. All of these work similarly to Column, ScrollableColumn and Lazy Column, respectively. Therefore, in order to make this blog provide more information, we do not include this section. However, you can find these codes from our open source project.

Box

Box is a composable layout for placing children relative to its edges. Initially, Stack was used instead of Box. But now, Stack is not recommended and Box is introduced.

As the name implies, the child is placed inside the parents. Children in the box are drawn in the specified order, and if the child elements are smaller than the parent, they are placed in the box by default according to alignment.

Here is an example of Box:

@Composable
fun SimpleBoxComponent() {
    Box(modifier = ().padding()) {
        Image(imageResource(.mindorks_cover))
        Text(
            modifier = (start = , top = ),
            text = "I am a text over Image",
            fontSize = ,
            color = 
        )
    }
}

Button

Button is used to perform certain actions when the user clicks.

Here is an example of a simple Button:

@Composable
fun SimpleButtonComponent() {
    val context = 
    Button(
        onClick = {
            (context, "Thanks for clicking!", Toast.LENGTH_LONG).show()
        },
        modifier = ().fillMaxWidth()
    ) {
        Text("Click Me")
    }
}

Here, Text is used to place some text on the button, and the onClick callback is used to listen for the button's click event.

By passing various parameters to Button, you can customize them to a large extent. Some of them are:

  • Make a rounded corner Button:
shape = RoundedCornerShape()
  • Make a Button with borders:
border = BorderStroke(width = , brush = SolidColor())

Similarly, you can add some icons to the button, apply colors to the button, disable the button, make outline buttons, make IconButtons, make FABs, and more. You can check out our open source project for more examples.

Card

Card is a composable layout for making CardViews.

Here are the same examples:

@Composable
fun SimpleCardComponent() {
    Card(
        backgroundColor = Color(()),
        modifier = ().fillMaxWidth()
    ) {
        Text(
            text = "Simple Card",
            textAlign = ,
            style = TextStyle(
                fontSize = 
            ),
            modifier = ()
        )
    }
}

Clickable

You can use Clickable to make composable reactions to users. Clickable supports single click, double click and long press.

Here are the same examples:

@Composable
fun SimpleTextComponent() {
    val context = 
    Text(
        text = "Click Me",
        textAlign = ,
        color = ,
        modifier = ().fillMaxWidth().clickable(onClick = {
            (context, "Thanks for clicking! I am Text", Toast.LENGTH_SHORT).show()
        }, onLongClick = {
            (context, "Thanks for LONG click! I am Text", Toast.LENGTH_SHORT).show()
        }, onDoubleClick = {
            (context, "Thanks for DOUBLE click! I am Text", Toast.LENGTH_SHORT).show()
        })
    )
}

Likewise, you can make the card clickable.

Image

To display images, we can use Image to composable.

@Composable
fun SimpleImageComponent() {
    // Image is a composable that is used to display some image.
    val image = imageResource(.mindorks_cover)
    Column(
        modifier = ()
    ) {
        Image(image)
    }
}

You can also make rounded corner images using the following code:

Image(
    image,
    modifier = ().clip(shape = RoundedCornerShape()),
    contentScale = 
)

Alert Dialog

As the name suggests, AlertDialog is used to display some important messages (and there may be some actions) to the user in the form of a dialog box.

We have titles, text, confirm buttons and close buttons in AlertDialog.

@Composable
fun AlertDialogComponent() {
    val openDialog = remember { mutableStateOf(true) }
    if () {
        AlertDialog(
            onDismissRequest = {  = false },
            title = { Text(text = "Alert Dialog") },
            text = { Text("Hello! I am an Alert Dialog") },
            confirmButton = {
                TextButton(
                    onClick = {
                         = false
                        /* Do some other action */
                    }
                ) {
                    Text("Confirm")
                }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                         = false
                        /* Do some other action */
                    }
                ) {
                    Text("Dismiss")
                }
            },
            backgroundColor = ,
            contentColor = 
        )
    }
}

Material AppBar

To display AppBar in Android apps, you can use TopAppBar or BottomAppBar to combine in the app. Here you can have the title (usually the application name), some navigation icons, and some actions on the AppBar.

@Composable
fun TopAppBarComponent() {
    TopAppBar(
        modifier = ().fillMaxWidth(),
        title = { Text("App Name") },
        navigationIcon = {
            IconButton(onClick = { /* doSomething() */ }) {
                Icon()
            }
        },
        actions = {
            IconButton(onClick = { /* doSomething() */ }) {
                Icon()
            }
            IconButton(onClick = { /* doSomething() */ }) {
                Icon()
            }
        }
    )
}

Similarly, we can use BottomAppBar as well.

Material BottomNavigation

BottomNavigation is used to display some important actions of the application at the bottom of the screen for easy access by users. To add the item to a BottomNavigation we need to use the BottomNavigationItem composable item.

@Composable
fun BottomNavigationWithLabelComponent() {
    var selectedItem by remember { mutableStateOf(0) }
    val items = listOf("Home", "Blogs", "Profile")
    BottomNavigation(
        modifier = ().fillMaxWidth(),
        backgroundColor = ,
        contentColor = 
    ) {
         { index, item -&gt;
            BottomNavigationItem(
                label = { Text(text = item) },
                icon = { Icon() },
                selected = selectedItem == index,
                onClick = { selectedItem = index }
            )
        }
    }
}

To use the bottom navigation without labels, you can alwaysShowLabels = false in BottomNavigationItem.

Material Checkbox

Use the checkbox when we have 2 options and the user can select or deselect options.

@Composable
fun SimpleCheckboxComponent() {
    val checkedState = remember { mutableStateOf(true) }
    Row {
        Checkbox(
            checked = ,
            modifier = (),
            onCheckedChange = {  = it },
        )
        Text(text = "Checkbox Example", modifier = ())
    }
}

onCheckedChangecallback is used to identify changes in Checkbox.

Material ProgressBar

ProgressBar is used to display some progress that is happening in the application. For example, download progress or load data from the server.

We can make a circular progress bar or a linear progress bar.

Here is an example of a loop progress bar:

@Composable
fun SimpleCircularProgressComponent() {
    CircularProgressIndicator(
        modifier = ()
    )
}

You can also set progress = 0.4f using .

Again, you can use LinearProgressIndicator as well.

Material Slider

Slider is used to select some values ​​from a series of values. For example, you can increase/decrease the volume through the volume slider, increase/decrease brightness through the brightness slider, etc.

The slider can be linear or there can be some discrete values, i.e. you can slide to select only allowed values, such as selecting only integer values.

@Composable
fun SimpleSliderComponent() {
    var sliderValue by remember { mutableStateOf(0.4f) }
    Slider(
        value = sliderValue,
        modifier = (),
        onValueChange = { newValue -&gt;
            sliderValue = newValue
        }
    )
    Text(
        text = "Slider value: $sliderValue",
        modifier = ()
    )
}

Similarly, you can make a step slider by passing the steps parameter to the Slider.

Material Snackbar

Snackbar is used to display some information at the bottom of the screen and place it on all UI elements.

@Composable
fun SimpleSnackbarComponent() {
    Snackbar(
        modifier = (),
        text = {
            Text(text = "I'm a Simple Snackbar")
        }
    )
}

You can also add some actions to Snackbar using the following methods:

action = {
    Text(text = "OK", style = TextStyle(color = ))
}

Custom View

In Compose, we can also make a Canvas, and on the canvas we can draw various shapes such as circles, rectangles, arcs, etc.

@Composable
fun CustomViewComponent() {
    Canvas(modifier = ().padding()) {
        drawRect(
            color = ,
            // topLeft is the coordinate of top-left point
            topLeft = Offset(0f, 0f),
            size = Size(800f, 400f)
        )
        drawArc(
            ,
            startAngle = 0f,
            sweepAngle = 120f,
            useCenter = true,
            size = Size(600f, 600f),
            topLeft = Offset(300f, 300f)
        )
    }
}

Crossfade animation

We can also use animations in Compose. For example, we can use Crossfade animations by using Crossfade Composable.

@Composable
fun CrossFadeAnimation() {
    val colors = listOf(, , , )
    var current by remember { mutableStateOf(colors[0]) }
    Column(modifier = ()) {
        Crossfade(current = current) { color -&gt;
            Box(().clickable(
                onClick = {
                    current = ()
                }
            ).background(color))
            Text(
                modifier = (),
                textAlign = ,
                text = "Click To See"
            )
        }
    }
}

Here, the color of the Box will change with the Crossfade animation when the Box is clicked.

This is what this tutorial is about. You can try many other examples of Jetpack Compose.

Translation link:/jetpack-compose-tutorial

The above is the detailed explanation of the Jetpack Compose step-by-step guide tutorial. For more information about the Jetpack Compose step-by-step tutorial, please pay attention to my other related articles!