This article will show how to rebuild the default Flutter counter app using Jetpack Compose.
The article is divided into two sections:
UI, where the app's user interface will be built.
State, where the state of the app's counter will be implemented.
Below is a screenshot of the default Flutter counter app:
This is what we'll rebuild with Jetpack Compose.
The first step is to download a Jetpack Compose supported Android Studio version (at this time of writing, that is the Arctic Fox version).
The next step is to create an empty compose activity by clicking on File > New Project and selecting Empty Compose Activity
from the templates shown.
Click on Next
.
Then we enter our application name. For this article, we'll use "ComposeCounterApp".
Click on Finish
and it should open the ComposeCounterApp
project.
1. UI
Open the MainActivity.kt
file. It should contain the default Compose Activity code.
We'll remove the code and update the content of MainActivity.kt
to this below:
package com.example.composecounterapp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import com.example.composecounterapp.ui.theme.ComposeCounterAppTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeCounterAppTheme {
ComposeCounter()
}
}
}
}
@Composable
fun ComposeCounter() {
Scaffold(
content = {}
)
}
On running the project on a device, an empty screen is displayed. This is because ComposeCounter
only contains a Scaffold composable with empty content.
Next, we'll add the code for the app bar. We'll use the TopAppBar composable and specify the title, the background colour, and the content colour.
Update the ComposeCounter
function to this.
@Composable
fun ComposeCounter() {
val primaryColor = Color(0xFF2196F3)
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Compose Home Demo Page") },
backgroundColor = primaryColor,
contentColor = Color.White
)
},
content = {}
)
}
This will show the following screen:
Next, we'll add the content of the screen. And for this, we'll pass a Column with two Texts to the Scaffold as content
. The column items will be aligned in the center and we'll pass modifiers to ensure the Column fills the screen horizontally and vertically.
@Composable
fun ComposeCounter() {
val primaryColor = Color(0xFF2196F3)
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Compose Home Demo Page") },
backgroundColor = primaryColor,
contentColor = Color.White
)
},
content = {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
) {
Text(
text = "You have pushed this button this many times",
)
Text(
text = "0",
style = MaterialTheme.typography.h4,
)
}
},
)
}
And now we'll have this showing on our device.
The last thing to add for the UI is the floating action button, which we can specify on the Scaffold by passing a FloatingActionButton composable to the Scaffold as floatingActionButton
.
We do that by updating the ComposeCounter
composable to the code below:
@Composable
fun ComposeCounter() {
val primaryColor = Color(0xFF2196F3)
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Compose Home Demo Page") },
backgroundColor = primaryColor,
contentColor = Color.White
)
},
content = {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
) {
Text(
text = "You have pushed this button this many times",
)
Text(
text = "0",
style = MaterialTheme.typography.h4,
)
}
},
floatingActionButton = {
FloatingActionButton(
onClick = {},
backgroundColor = primaryColor,
contentColor = Color.White
) {
Icon(Icons.Filled.Add, contentDescription = "Add button")
}
},
)
}
And now, we'll have our complete UI! ๐
2. State
Currently, the app displays "0" as the counter state and we need to make sure that clicking the floating action button increments that state.
We need a variable to hold the state of the counter.
From State and Jetpack Compose,
There are three ways to declare a MutableState object in a composable:
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
We'll be using the second one.
At the top of the ComposeCounter
composable, we'll add a variable counter
to hold the state of the counter and set its default value to zero.
We'll also use this counter
variable in the Text composable instead of "0".
And now the ComposeCounter
will have the following code:
@Composable
fun ComposeCounter() {
val primaryColor = Color(0xFF2196F3)
var counter: Int by remember { mutableStateOf(0) }
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Compose Home Demo Page") },
backgroundColor = primaryColor,
contentColor = Color.White
)
},
content = {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
) {
Text(
text = "You have pushed this button this many times",
)
Text(
text = counter.toString(),
style = MaterialTheme.typography.h4,
)
}
},
floatingActionButton = {
FloatingActionButton(
onClick = {
},
backgroundColor = primaryColor,
contentColor = Color.White
) {
Icon(Icons.Filled.Add, contentDescription = "Add button")
}
},
)
}
There will be no visible change on the screen for now.
The next thing to do is to increment the counter when the floating action button is clicked.
We can do that in the onClick
callback of the FloatingActionButton
.
The code will be updated to this:
@Composable
fun ComposeCounter() {
val primaryColor = Color(0xFF2196F3)
var counter: Int by remember { mutableStateOf(0) }
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Compose Home Demo Page") },
backgroundColor = primaryColor,
contentColor = Color.White
)
},
content = {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
) {
Text(
text = "You have pushed this button this many times",
)
Text(
text = counter.toString(),
style = MaterialTheme.typography.h4,
)
}
},
floatingActionButton = {
FloatingActionButton(
onClick = {
counter += 1
},
backgroundColor = primaryColor,
contentColor = Color.White
) {
Icon(Icons.Filled.Add, contentDescription = "Add button")
}
},
)
}
Now the app increments the counter when the floating action button is tapped as seen in the gif below:
And now the app is complete.
The complete code for the MainActivity.kt
is this below:
package com.example.composecounterapp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.example.composecounterapp.ui.theme.ComposeCounterAppTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeCounterAppTheme {
ComposeCounter()
}
}
}
}
@Composable
fun ComposeCounter() {
val primaryColor = Color(0xFF2196F3)
var counter: Int by remember { mutableStateOf(0) }
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Compose Home Demo Page") },
backgroundColor = primaryColor,
contentColor = Color.White
)
},
content = {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
) {
Text(
text = "You have pushed this button this many times",
)
Text(
text = counter.toString(),
style = MaterialTheme.typography.h4,
)
}
},
floatingActionButton = {
FloatingActionButton(
onClick = {
counter += 1
},
backgroundColor = primaryColor,
contentColor = Color.White
) {
Icon(Icons.Filled.Add, contentDescription = "Add button")
}
},
)
}
The complete project can be found on Github.