Jetpack Compose UI Development Guide
Jetpack Compose UI Development Guide
Server Architecture
Lecture 3
Lecturer: Ivan Ng
[Link]
What is Jetpack Compose?
● A modern, fully declarative UI toolkit for building native Android apps.
● Simplifies UI development by using Kotlin to create UI components
programmatically.
● With Compose, you can build your UI by defining a set of functions, called
composable functions, that take in data and describe UI elements.
Why Use Jetpack Compose?
● Reduces development time.
● Encourages clean, maintainable code.
● Future-proof for modern Android app design.
Composable functions
● Composable functions are the basic building block of a UI in Compose.
● A composable function:
○ Describes some part of your UI.
○ Doesn't return anything.
○ Takes some input and generates what's shown on the screen.
● A Composable function is called such because it represents a reusable,
self-contained piece of UI that can be composed together with other
composables to create complex interfaces.
● That’s why it’s called composable
Composable functions
● The Composable function is annotated with the @Composable annotation.
● All composable functions must have this annotation.
● This annotation informs the Compose compiler that this function is intended to
convert data into UI.
Composable functions
● This code snippet is an example of a simple composable function that is
passed data (the name function parameter) and uses it to render a text
element on the screen.
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
Annotations
● Annotations are means of attaching extra information to code.
● This information helps tools like the Jetpack Compose compiler, and other
developers understand the app's code.
● An annotation is applied by prefixing its name (the annotation) with the @
character at the beginning of the declaration you are annotating.
● Different code elements, including properties, functions, and classes, can be
annotated.
Annotations
● Jetpack Compose includes a wide range of built-in annotations, you have
already seen @Composable
Preview
● Android Studio lets you preview your @Composable
composable functions within the IDE, fun Greeting(name: String) {
instead of installing the app to an Android Text(text = "Hello $name!")
device or emulator. }
● The composable function must provide
default values for any parameters to @Preview(showBackground = true)
preview it. @Composable
● For this reason, it is recommended not to fun BirthdayCardPreview() {
preview the Greeting() function directly.
HappyBirthdayTheme {
● Instead, you need to add another function,
Greeting("Android")
the BirthdayCardPreview() function in this
}
case, that calls the Greeting() function with
}
an appropriate parameter.
Preview
The Overall Calling Flow
● The setContent function acts as a
bridge between the Android
framework and Jetpack Compose.
● It initializes the Compose runtime
within the activity and starts the
composition process (the process
of rendering and managing the UI
tree).
● It can call composables (e.g.
Greeting) and only composables
can call composables
subsequently
UI using Jetpack
Compose
Modifiers
● Most Compose UI elements such as Surface and Text accept an optional
modifier parameter.
● The padding modifier will apply an amount of space around the element it
decorates. You can create a padding modifier with Modifi[Link]()
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Surface(color = [Link]) {
Text(
text = "Hello $name!",
modifier = [Link]([Link])
)
}
}
Modifiers
● There are many modifiers
● Reference: [Link]
Creating Columns and Rows
● The three basic standard layout elements in Compose are Column, Row and Box.
Creating Columns and Rows
● Most Compose UI elements such as Surface and Text accept an optional
modifier parameter.
● The padding modifier will apply an amount of space around the element it
decorates. You can create a padding modifier with Modifi[Link]()
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Surface(color = [Link]) {
Column {
Text("Hello")
Text("Android")
}
}
}
@Composable
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Surface(
color = [Link],
modifier = [Link](vertical = [Link], horizontal = [Link])
) {
Column(modifier = [Link]().padding([Link])) {
Text(text = "Hello ")
Text(text = name)
}
}
}
@Composable
fun MyApp(
modifier: Modifier = Modifier,
Adding a button ) {
names: List<String> = listOf("World", "Compose")
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Surface(
color = [Link],
modifier = [Link](vertical = [Link], horizontal = [Link])
) {
Row(modifier = [Link]([Link])) {
Column(modifier = [Link](1f)) {
Text(text = "Hello ")
Text(text = name)
}
ElevatedButton(
onClick = { /* TODO */ }
) {
Text("Show more")
}
}
}
}
Adding a button
● There are different types of buttons.
State in Compose
● The state of the item: Store some value somewhere that indicates whether
each item is expanded or not
@Composable
fun MyApp(
modifier: Modifier = Modifier,
State in Compose ) {
names: List<String> = listOf("World", "Compose")
State in Compose ) {
names: List<String> = listOf("World", "Compose")
State in Compose ) {
names: List<String> = listOf("World", "Compose")
State in Compose ) {
names: List<String> = listOf("World", "Compose")
change @Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
● This can be done by a var expanded by remember {mutableStateOf(false)}
Surface(
lambda function color = [Link],
modifier = [Link](vertical = [Link], horizontal = [Link])
) {
Row(modifier = [Link]([Link])) {
Column(modifier = [Link](1f)) {
Text(text = "Hello ")
Text(text = name)
}
ElevatedButton(
onClick = { expanded = !expanded }
) {
Text(if (expanded) "Show less" else "Show more")
}
}
}
}
@Composable
fun MyApp(
modifier: Modifier = Modifier,
State in Compose ) {
names: List<String> = listOf("World", "Compose")
when it is expanded }
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
var expanded by remember {mutableStateOf(false)}
var extraPadding = if (expanded) [Link] else [Link]
Surface(
color = [Link],
modifier = [Link](vertical = [Link], horizontal = [Link])
) {
Row(modifier = [Link]([Link])) {
Column(modifier = [Link](1f).padding(bottom = extraPadding)
) {
Text(text = "Hello ")
Text(text = name)
}
ElevatedButton(
onClick = { expanded = !expanded }
) {
Text(if (expanded) "Show less" else "Show more")
}
}
}
}
Display a long list
import [Link]
● So far, we displayed two import [Link]
greetings. // ...
● For reference:
● [Link]
Persisting State
● The remember function works when the composable is kept in the Composition.
● When you rotate, the whole activity is restarted so all state is lost.
● Instead, you can use rememberSaveable.
● This will save each state surviving configuration changes (such as rotations) and process
death.
import [Link]
// ...
● rememberSaveable works like remember, but it also saves the state in a Bundle using
the SavedStateHandle or the saved instance state mechanism of Android. This means
the state is retained during configuration changes (like screen rotation) and process
recreation.
Difference: = remember versus by remember
When you use = remember, you are assigning the @Composable
fun ExampleWithEqualSign() {
result of the remember function to a variable or val state = remember { mutableStateOf(0) } // Assigning remember to a variable
property directly. You need to access or modify the
Column {
value explicitly.
Text("Value: ${[Link]}") // Access [Link] explicitly
Button(onClick = { [Link]++ }) {
Text("Increment")
}
}
}
The list of quanty options is stored as a The list of possible flavors is stored as a list of Pickup options come from a list returned
list of pairs in [Link]. string resource IDs in [Link]. by the pickupOptions() function in
OrderViewModel.
UI Elements