Introduction
In Android app development, creating interactive UI elements is essential for enhancing user experience. Jetpack Compose, Android’s modern toolkit for building native UI, offers developers a declarative way to design interfaces with reactive components. One common task in UI design is making components clickable, such as displaying a counter that updates on tap or click. In Jetpack Compose, implementing click behavior is streamlined and straightforward, allowing you to control how elements respond to user interactions with minimal boilerplate.
This article explains how to create clickable cards in Jetpack Compose. In the example code, we build two cards that react differently when clicked: one card increments a counter when tapped, and the other decrements it. By following this breakdown, you’ll gain a solid understanding of how to use Jetpack Compose to handle user interactions and dynamically update the UI.
Code Breakdown: Main Activity Setup
The entry point of the app is the MainActivity
class, which extends AppCompatActivity
. Within the onCreate
method, the setContent
function is used to define the app’s content. Here, setContent
is called with the GetScaffold
composable function, which initializes the app's main screen layout. This setup reflects the flexibility of Jetpack Compose, as it allows the UI to be defined directly within Kotlin functions rather than XML.
The GetScaffold
function wraps the app’s UI within a Scaffold
composable, which is designed to handle basic UI structure components like the top bar, bottom bar, floating action buttons, and more. In this case, the Scaffold
includes a TopAppBar
that displays the title “Compose - Card Clickable” with a background color. Additionally, the main content of the screen is set to the MainContent
composable, defining the core interactive elements.
Main Content Structure
The MainContent
function is where the primary UI elements are organized. Inside, a Column
composable is used to structure the layout vertically. This Column
is set to take up the entire screen size with fillMaxSize
, and it includes padding around the edges for visual spacing. By specifying Arrangement.SpaceEvenly
, the child elements within the Column
are spaced evenly, providing a balanced layout. horizontalAlignment
is set to Alignment.CenterHorizontally
, ensuring the elements are aligned in the center of the screen horizontally.
A mutable state variable counter
is created using remember
and mutableStateOf
. This variable tracks the count displayed in the cards, and its value is updated in response to user interactions. Using remember
allows the state to persist across recompositions, so the counter value is retained as users interact with the app.
First Card: Incrementing the Counter
The first Card
composable is styled with a background color, elevation, and rounded corners. It spans the full width of the screen, with a fixed height of 150 dp. The clickable
modifier is applied to this card, which defines the interaction behavior when the card is tapped. Inside the clickable
block, the counter
variable is incremented by one each time the card is clicked.
Inside the card, a Box
composable is used to center a Text
composable, which displays the current value of counter
. This is an example of Jetpack Compose’s flexibility, as UI components and data are tightly integrated. Whenever the counter value changes, Jetpack Compose automatically re-composes the UI, updating the displayed count in real-time without any manual intervention.
Second Card: Decrementing the Counter
The second Card
is similar in layout and style to the first but demonstrates an alternative method for handling clicks. Instead of using the clickable
modifier, this card uses the pointerInput
modifier combined with the detectTapGestures
function to manage touch gestures. This approach offers more customization for tap events, as it allows for separate gesture detection, including long-press, double-tap, and more. In this case, onTap
is defined to decrement the counter
by one each time the card is tapped.
This card has a red background color to visually distinguish it from the first. By using pointerInput
with detectTapGestures
, the code showcases how to go beyond basic clicks and implement more advanced gesture recognition in Jetpack Compose. This flexibility is particularly useful in scenarios where you want to provide custom interactions beyond simple clicks.
Preview Function
The ComposablePreview
function serves as a preview setup, allowing developers to view the UI within Android Studio’s preview pane. Although commented out in this code, invoking GetScaffold()
inside ComposablePreview
would enable a visual preview of the app’s main screen without needing to run the app on an emulator or physical device. This feature is extremely useful for speeding up the UI design process.
Summary
This example demonstrates how to create interactive cards in Jetpack Compose by utilizing modifiers like clickable
and pointerInput
to handle user interactions. Jetpack Compose’s declarative UI approach simplifies state management and reactivity, making it easier for developers to design responsive interfaces. With just a few lines of code, it’s possible to create complex behavior, such as updating a counter in real-time based on user clicks.
Whether you’re building a simple counter or a more complex app, understanding how to handle click events and manage state in Jetpack Compose is essential. This example serves as a foundational pattern for building interactive elements, illustrating the flexibility and power of Jetpack Compose in creating modern Android applications.
package com.cfsuman.jetpackcompose
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
GetScaffold()
}
}
@Composable
fun GetScaffold(){
Scaffold(
topBar = {TopAppBar(
title = {Text(
"Compose - Card Clickable",
color = Color.White)},
backgroundColor = Color(0xFF58427C)) },
content = {MainContent()},
backgroundColor = Color(0xFFEDEAE0)
)
}
@Composable
fun MainContent(){
var counter by remember { mutableStateOf(0)}
Column(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.clickable{
counter ++
},
backgroundColor = Color(0xFF319177),
elevation = 4.dp,
shape = RoundedCornerShape(24.dp),
) {
Box(Modifier.wrapContentSize(Alignment.Center)) {
Text(
text = "$counter",
style = MaterialTheme.typography.h1
)
}
}
Card(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.pointerInput(UInt){
detectTapGestures(
onTap = { counter-- }
)
},
backgroundColor = Color(0xFFE95C4B),
elevation = 4.dp,
shape = RoundedCornerShape(24.dp),
) {
Box(Modifier.wrapContentSize(Alignment.Center)) {
Text(
text = "$counter",
style = MaterialTheme.typography.h1
)
}
}
}
}
@Preview
@Composable
fun ComposablePreview(){
//GetScaffold()
}
}
- jetpack compose - Card padding
- jetpack compose - Card background color
- jetpack compose - Card alignment
- jetpack compose - ModalDrawer example
- jetpack compose ktor - How to post data
- jetpack compose - Kotlinx serialization pretty print
- jetpack compose - Kotlinx serialization lenient parsing
- jetpack compose - Kotlinx serialization handle null values
- jetpack compose - Kotlinx serialization not encode null values
- jetpack compose - Kotlinx serialization encode to string
- jetpack compose - Kotlinx serialization allow special floating point values
- jetpack compose - Kotlinx serialization parse to json element
- jetpack compose - Kotlinx serialization build json element
- jetpack compose - How to use kotlin flow
- jetpack compose - Random number flow