Jetpack Compose Basics Code Lab

The codelab can be found here: link

Courses

The first 3 parts of the lab went through the set up process and initial Composable functions. The function below is the basic function that is used to create a text view. I've had some practice before so this was nothing to write home about.

@Composable
private fun Greeting(name: String) {
   Text(text = "Hello $name!")
}

Part 4: Tweaking the UI

The Surface Composable

The next part of the lab went through how to change the UI.

The code snippet supplied was:

@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colorScheme.primary) {
        Text (text = "Hello $name!")
    }
}

The interesting part for me was the Surface composable. My newfound understanding was that the Surface composable is used to change the background color of the text. Changing the color of the surface changes the background color of the text based on the theme's onXXX color.

Modifiers

Modifiers in Compose are used to change the layout of the UI. The code snippet supplied was:

@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colorScheme.primary) {
        Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
    }
}

Here, the Modifier.padding(24.dp) shows how to add padding to the text. From this, we can see the syntax of how the units are converted to Modifier-acceptable values 24.dp.

Part 5: Reusable Composables

Note from the lab: As a best practice, your function should include a Modifier parameter that is assigned an empty Modifier by default.

@Composable
private fun MyApp(modifier: Modifier = Modifier) {
    Surface(
        modifier = modifier,
        color = MaterialTheme.colorScheme.background
    ) {
        Greeting("Android")
    }
}

Part 6: Columns and Rows

The next part of the lab went through how to use Column and Row composable functions. They also included a third one called Box which is used to stack elements on top of each other.

I've played around with these earlier as well so I feel confident with using these composables - setting the size, padding, nesting, etc.

This part adds a button to the layout and provides a basic layout for the Button composable:

Button(
    onClick = { }
) {
    Text("Show less")
}

However, they point out that the Material Design Buttons spec give you other options to work with out of the box:

  • Button
  • ElevatedButton
  • FilledTonalButton
  • OutlinedButton
  • TextButton

Part 7: State

State and MutableState are interfaces that hold some value and trigger UI updates (recompositions) whenever that value changes.

To preserve state across recompositions, remember the mutable state using remember. remember is used to guard against recomposition, so the state is not reset.

@Composable
fun Greeting() {
    val expanded = remember { mutableStateOf(false) }
    ...
}

This is how the state is read and mutated:

ElevatedButton(
    onClick = { expanded.value = !expanded.value },
) {
   Text(if (expanded.value) "Show less" else "Show more")
}

I find it interesting that you can make calculations based on the mutable state.

@Composable
private fun Greeting(name: String) {

    val expanded = remember { mutableStateOf(false) }

    val extraPadding = if (expanded.value) 48.dp else 0.dp
...
}

Part 8: State Hoisting

State that is read or modified by multiple functions should live in a common ancestor

Part 9: Lazy Lists

LazyColumn doesn't recycle its children like RecyclerView. It emits new Composables as you scroll through it and is still performant, as emitting Composables is relatively cheap compared to instantiating Android Views.

Really? How come?

Part 10: Persisting State

The remember function works only as long as the composable is kept in the Composition. Instead of using remember you can use rememberSaveable. This will save each state surviving configuration changes (such as rotations) and process death.

var shouldShowOnboarding by rememberSaveable { mutableStateOf(true) }

Part 11: Animation

This code uses the animateDpAsState function to animate the padding of the Column composable. It returns a State object whose value will continuously be updated by the animation until it finishes. It takes a "target value" whose type is Dp.

@Composable
private fun Greeting(name: String) {

    var expanded by remember { mutableStateOf(false) }

    val extraPadding by animateDpAsState(
        if (expanded) 48.dp else 0.dp,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        )
    )

    Surface(
    ...
            Column(modifier = Modifier
                .weight(1f)
                .padding(bottom = extraPadding.coerceAtLeast(0.dp))

    ...

    )
}

Part 12: Styling and Theming

Just like my own activity to practice building my own custom Themes.

Done!