Skip to content
DeveloperMemos

How to Create a Counter in Jetpack Compose

Kotlin, Jetpack Compose3 min read

Jetpack Compose is a modern toolkit for building native Android UIs using a declarative approach, and it's quickly becoming the preferred way of building UIs on Android. With Jetpack Compose, you can write your UI code in Kotlin, which means you can take advantage of the full power of the language to create complex, dynamic, and responsive user interfaces.

In this tutorial, I'll walk you through the steps of building a simple counter app using Jetpack Compose. We'll cover everything from setting up the project, to defining state, to laying out the UI, to handling user input. Anyway let's get right into it.

Creating the Project

First of all you need to open Android Studio and create a new 'Empty Activity' project. Make sure you select the template that has the Compose symbol in the middle of the screen.

When the project setup is complete you should have a MainActivity.kt file that contains a call to setContent inside the overriden onCreate function. All the initial Compose code will be inside setContent and all you'll really have is a Surface Composable and an initial custom Composable called Greeting. You can go ahead and delete the Greeting Composable but you may want to try running your project first just to make sure everything compiles and runs beforehand.

Adding the State for the Counter

First of all we're going to need something to contain the state of the counter. You can define this value like this:

1var number by remember { mutableStateOf(0) }

You'll need to import these too, it should give you the option to do this in Android Studio automatically:

1import androidx.compose.runtime.getValue
2import androidx.compose.runtime.mutableStateOf
3import androidx.compose.runtime.remember
4import androidx.compose.runtime.setValue

Just as a short explainer, mutableStateOf creates the state and the initial value. This is wrapped inside remember which will keep track of any changes that are made through configuration changes, but more importantly it will also keep track of the current value through recompositions. A simpler way of saying this is that the value won't be reset if the Composable is rendered again. If you are familiar with React Hooks it is similar to useState.

Adding an Initial Container(Column)

Alright so if you didn't delete the Greeting Composable go ahead and do that now. Don't delete the Surface Composable though. We'll keep that and we'll add a Column inside it to replace Greeting:

1Column(
2 verticalArrangement = Arrangement.spacedBy(
3 space = 10.dp,
4 alignment = Alignment.CenterVertically
5 ),
6 horizontalAlignment = Alignment.CenterHorizontally
7) {
8 // Add remaining UI here
9}

After we're done here we're going to add buttons and some text to where the comment is now. Column is a Composable that is a bit like a container. You can add children inside Column and it will lay them out vertically. I've added alignment and arrangement values to Column so that the children inside it will be centered vertically and horizontally and also have 10 dp of space between the vertically.

Adding a Label(Text)

Okay let's add a Text Composable inside Column:

1Text("Current number: $number")

As you can see this references the state that we created earlier number. We haven't added any buttons yet so if you build the project and run it now it will just show "Current number: 0" and won't change. We'll get to that in the next step.

Adding some Buttons

Now we're almost finished, we just need the buttons. Below the Text Composable(inside Column) let's add two buttons, one for incrementing number and another for decrementing number:

1Button(onClick = { number++ }) {
2 Text("Increment")
3}
4Button(onClick = { number-- }) {
5 Text("Decrement")
6}

As you can see above we define Text Composables to use as labels for the Button. Aside from that we have defined onClick handlers inside the buttons too that either increase or decrease our state(number). Now if you build and run the project you should be able to tap the buttons and the new value should be displayed inside the Text Composable we added in the last step.

Bonus Round: Add a Reset Button

You don't need to do this but you can also add a reset button under the two buttons we created above. It doesn't really need much of an explanation because I explained most of how everything works above. Here's the code though:

1Button(onClick = { number = 0 }) {
2 Text("Reset")
3}

The Final Code

Here's the complete code for the steps above so if you are having issues you can copy and paste this directly into the project, I'll include the imports too:

1import android.os.Bundle
2import androidx.activity.ComponentActivity
3import androidx.activity.compose.setContent
4import androidx.compose.foundation.layout.Arrangement
5import androidx.compose.foundation.layout.Column
6import androidx.compose.foundation.layout.fillMaxSize
7import androidx.compose.material3.Button
8import androidx.compose.material3.MaterialTheme
9import androidx.compose.material3.Surface
10import androidx.compose.material3.Text
11import androidx.compose.runtime.getValue
12import androidx.compose.runtime.mutableStateOf
13import androidx.compose.runtime.remember
14import androidx.compose.runtime.setValue
15import androidx.compose.ui.Alignment
16import androidx.compose.ui.Modifier
17import androidx.compose.ui.unit.dp
18import com.example.testcompose.ui.theme.TestComposeTheme
19
20class MainActivity : ComponentActivity() {
21 override fun onCreate(savedInstanceState: Bundle?) {
22 super.onCreate(savedInstanceState)
23 setContent {
24 var number by remember { mutableStateOf(0) }
25
26 TestComposeTheme {
27 // A surface container using the 'background' color from the theme
28 Surface(
29 modifier = Modifier.fillMaxSize(),
30 color = MaterialTheme.colorScheme.background
31 ) {
32 Column(
33 verticalArrangement = Arrangement.spacedBy(
34 space = 10.dp,
35 alignment = Alignment.CenterVertically
36 ),
37 horizontalAlignment = Alignment.CenterHorizontally
38 ) {
39 Text("Current number: $number")
40 Button(onClick = { number++ }) {
41 Text("Increment")
42 }
43 Button(onClick = { number-- }) {
44 Text("Decrement")
45 }
46 Button(onClick = { number = 0 }) {
47 Text("Reset")
48 }
49 }
50 }
51 }
52 }
53 }
54}

Wrap Up

I hope this tutorial has given you a good introduction to Jetpack Compose and how to use it to build UIs on Android. While this example app was simple, the concepts we covered can be applied to more complex apps as well. Jetpack Compose is still a relatively new technology, but it's growing rapidly and becoming more stable with each release. It's an exciting time to be an Android developer, and I honestly can't wait to see Compose take over from XML in a big way.