Introduction
In modern Android development, Jetpack Compose has simplified UI design by enabling declarative programming, where UI components respond to changes in state directly. The example code provided showcases how to use Jetpack Compose in Kotlin to create a dynamic UI. Specifically, it demonstrates how to update the state of a variable across composable functions, illustrating the reactivity Compose offers for managing UI states. This code provides a great learning opportunity for understanding state management in Jetpack Compose, particularly how to share and update the state between different composables.
This example uses a simple scaffold layout in Compose, with a button that increments a counter displayed in the top app bar. By following a declarative pattern, it emphasizes the effective use of MutableState
to manage UI changes. Let's break down each part of the code to understand how the state is managed, passed, and displayed across different composables.
MainActivity Setup
The code begins with the MainActivity
class, which extends AppCompatActivity
. In the onCreate
method, the setContent
function is called, setting the main content of the activity to the GetScaffold
composable. This is a typical setup in Jetpack Compose, where setContent
is used instead of traditional XML layouts, allowing you to define the UI in a declarative manner. The use of @ExperimentalMaterialApi
annotations signifies that certain Material Design components are used, which might be in a beta or experimental phase, ensuring compatibility and functionality in Compose.
The GetScaffold
composable serves as the root layout of the UI. Inside GetScaffold
, a mutable state favCounter
is initialized using remember { mutableStateOf(0) }
. This mutable state, of type MutableState<Int>
, holds the value of the counter, starting at 0, and can be shared across multiple composables to keep the UI consistent. This setup enables favCounter
to be passed to child composables, where it can be modified, triggering recompositions wherever it’s used in the UI.
TopAppBarContent Composable
The TopAppBarContent
composable defines the top app bar, where favCounter
is displayed. This function takes favCounter
as a parameter, allowing it to access the current count value. Within this function, a TopAppBar
component is used to create the app bar layout. Inside the actions
parameter, a Box
with specific styling is created to display the value of favCounter
. The Box
is centered, with rounded corners and a light background color. The actual counter value is displayed in bold, with the text derived from favCounter.value
.
By passing favCounter
as a parameter, TopAppBarContent
directly accesses and reflects any changes to the counter. When favCounter
is updated elsewhere, Compose automatically triggers a re-composition of TopAppBarContent
, ensuring that the displayed counter is always up-to-date. This feature highlights one of Jetpack Compose’s core principles: reactivity based on state changes.
MainContent Composable
The main interactive part of the UI is defined in the MainContent
composable. Similar to TopAppBarContent
, MainContent
receives favCounter
as a parameter. This composable centers a button and text within the screen using a Box
layout with padding and alignment settings for the Column
child. The text simply informs the user about the purpose of this screen, and the button provides interactivity by allowing users to increment the counter.
The button’s onClick
lambda function contains favCounter.value++
, which increments the counter by 1. Since favCounter
is mutable, any updates to favCounter.value
cause re-composition in any composables that use it, including TopAppBarContent
. This setup illustrates how changes in one part of the UI (the button click) can propagate updates throughout the app, effectively demonstrating state sharing between composables.
Preview and Testing
The final section includes a @Preview
-annotated composable function, ComposablePreview
, which would allow developers to preview the UI within Android Studio. While GetScaffold()
is commented out in the preview, uncommenting it would allow developers to see how the entire layout appears without needing to run the app on an emulator or physical device. Previews are an essential feature in Jetpack Compose, making it easier to design and test UI layouts in real time.
Summary
This example highlights effective state management in Jetpack Compose, using a shared mutable state to control and synchronize UI elements across multiple composables. The use of MutableState
provides reactivity, ensuring that UI updates occur wherever the state is referenced, all without complex callback structures or listeners. By keeping favCounter
as a shared state, this setup ensures a clean, responsive, and efficient UI design.
This approach is particularly powerful in Android development as it reduces boilerplate code and makes the UI more intuitive and readable. By following this pattern, developers can build reactive, maintainable UIs in Jetpack Compose, leveraging Kotlin’s modern features for clean and concise state management.
package com.cfsuman.jetpackcompose
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
class MainActivity : AppCompatActivity() {
@ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
GetScaffold()
}
}
@ExperimentalMaterialApi
@Composable
fun GetScaffold(){
var favCounter = remember {mutableStateOf(0)}
Scaffold(
topBar = { TopAppBarContent(favCounter) },
content = {MainContent(favCounter)},
backgroundColor = Color(0xFFEDEAE0)
)
}
@ExperimentalMaterialApi
@Composable
fun TopAppBarContent(favCounter: MutableState<Int>) {
TopAppBar(
title = { Text(text = "Compose - Update state")},
backgroundColor = Color(0xFFC0E8D5),
actions = {
Box(modifier = Modifier
.size(36.dp)
.clip(CircleShape)
.background(Color(0xFFF0FFFF))
.padding(8.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "${favCounter.value}",
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.requiredWidth(12.dp))
}
)
}
@Composable
fun MainContent(favCounter: MutableState<Int>){
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
){
Column(
modifier = Modifier
.wrapContentSize(Alignment.Center)
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "Update state of another function variable",
style = MaterialTheme.typography.h6
)
Button(
onClick = {
favCounter.value++
}
) {
Text(text ="Update State")
}
}
}
}
@Preview
@Composable
fun ComposablePreview(){
//GetScaffold()
}
}
- jetpack compose - How to use ModalDrawer
- jetpack compose - How to use BadgeBox
- jetpack compose - TopAppBar navigation
- jetpack compose - TopAppBar actions
- jetpack compose - AndroidView click event
- jetpack compose - AndroidView modifier
- jetpack compose - How to use bottom navigation
- jetpack compose - Double click listener
- jetpack compose - Long click listener
- jetpack compose - Pass onClick event to function
- jetpack compose - How to hide system bars
- jetpack compose - Detect screen orientation change
- jetpack compose ktor - How to get api data
- jetpack compose - Icon from vector resource
- jetpack compose - IconButton from vector resource