Initial Movies UI

Flesh out the screens

Now we flesh out the screens, making a very simple (but inefficient) list, and a simple display.

First, we create a couple of helper components for consistency.

Display and Label are just Texts with some parameters set for styling and padding. Using this approach of creating wrapper functions gives us "extended" components.

Note

Note how Display has two padding modifiers. These are applied in the order they're defined. In this case, we apply an 8dp padding all around, and then an extra 16dp padding only at the start of the text. This causes the text to appear indented.

If we did something like

    Modifier
        .padding(8.dp)
        .border(2.dp, Color.Blue, RoundedCornerShape(4.dp))
        .padding(8.dp)

we'd see 8dp padding, a blue border inside it, and 8dp more padding inside the border. This is a much simpler system than the margin/padding combinations that existed with the old "views" UI approach.

We surround each of the screens with a Scaffold, which is a "slot api" composable function. This is an implementation of the Template Method Pattern, an algorithm with replaceable steps. The Scaffold defines an algorithm for managing several "slots" on the screen, areas like "stuff at the top", "stuff at the bottom", "stuff in between", etc. Scaffold defines the layout algorithm for measuring and placing elements in the specified position; you define what goes in those positions.

In this example, we define topBar, passing a lambda for what elements should appear in that section of the Scaffold. When the scaffold is performing its layout work, it calls that lambda to obtain the components, measures them, and places them. (Note that some slotted apis may conditionally execute lambdas such as these; for example, if you define a navigation drawer, it might not appear unless the user has swiped in from the side.)

Our topBar declares that a TopAppBar should appear. This is a common tool bar element, which can contain a title, navigation icon, and action icons. We'll do much more with it in later modules. Here we're just setting a title.

Our MovieDisplay sets up a Column as the main content of the Scaffold. Note the paddingValues that are passed to the content lambda. These define the padding you must use to avoid overlapping with other slots in the Scaffold.

Our Column is scrollable and stacks Labels and Displays to show the details of a movie.

MovieListUi is a bit more complex. It uses a Scaffold in the same way as MovieDisplay, but the contents of its column are dynamic. We iterate through the list of movies using forEach, and create a nice little Card for each movie, showing an icon and title.

We make the icon and title line up nicely by adding an alignment specification.

By defining onClick at the Card level, the user can click anywhere inside the card to select the movie. That click passes the movie for that card to onMovieClicked, informing the caller that a movie has been selected.

Be sure to define any literal strings that the user will see in app/src/main/res/values/strings.xml.

Code Changes

ADDED: /app/src/main/java/com/androidbyexample/movieui1/components/Display.kt
package com.androidbyexample.movieui1.componentsimport androidx.compose.foundation.borderimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.shape.RoundedCornerShapeimport androidx.compose.material3.MaterialThemeimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.unit.dp
@Composablefun Display( text: String,) { Text( text = text, style = MaterialTheme.typography.titleLarge, modifier = Modifier
.padding(8.dp) .padding(start = 16.dp)
.fillMaxWidth(), )}
ADDED: /app/src/main/java/com/androidbyexample/movieui1/components/Label.kt
package com.androidbyexample.movieui1.componentsimport androidx.annotation.StringResimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material3.MaterialThemeimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport androidx.compose.ui.unit.dp
@Composablefun Label( @StringRes textId: Int,) { Text( text = stringResource(id = textId), style = MaterialTheme.typography.titleMedium, modifier = Modifier .padding(8.dp) .fillMaxWidth(), )}
CHANGED: /app/src/main/java/com/androidbyexample/movieui1/screens/MovieDisplayUi.kt
package com.androidbyexample.movieui1.screens

import androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.rememberScrollStateimport androidx.compose.foundation.verticalScrollimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composable
import androidx.compose.ui.Modifierimport com.androidbyexample.movieui1.Movie
import com.androidbyexample.movieui1.Rimport com.androidbyexample.movieui1.components.Displayimport com.androidbyexample.movieui1.components.Label
@OptIn(ExperimentalMaterial3Api::class)@Composable
fun MovieDisplayUi(
//  movie: Movie    movie: Movie,) {
// Text(text = "Movie Display: ${movie.title}") Scaffold(
topBar = { TopAppBar( title = { Text(text = movie.title) } ) }
) { paddingValues ->
Column( modifier = Modifier .padding(paddingValues) .verticalScroll(rememberScrollState()) ) { Label(textId = R.string.title) Display(text = movie.title) Label(textId = R.string.description) Display(text = movie.description) }
}
}
CHANGED: /app/src/main/java/com/androidbyexample/movieui1/screens/MovieListUi.kt
package com.androidbyexample.movieui1.screens

//import androidx.compose.foundation.clickableimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.rememberScrollStateimport androidx.compose.foundation.verticalScrollimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Starimport androidx.compose.material3.Cardimport androidx.compose.material3.CardDefaultsimport androidx.compose.material3.CardElevationimport androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composable
import androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResourceimport androidx.compose.ui.unit.dpimport com.androidbyexample.movieui1.Movie
import com.androidbyexample.movieui1.Rimport com.androidbyexample.movieui1.components.Display
@OptIn(ExperimentalMaterial3Api::class)@Composable
fun MovieListUi(
    movies: List<Movie>,
    onMovieClicked: (Movie) -> Unit,
) {
//  Text(//      text = "Movie List",//      modifier = Modifier.clickable {//          onMovieClicked(movies[0])    Scaffold(        topBar = {            TopAppBar(                title = { Text(text = stringResource(R.string.movies)) }            )        },        modifier = Modifier.fillMaxSize()    ) { paddingValues ->        Column(            modifier = Modifier                .padding(paddingValues)                .fillMaxSize()                .verticalScroll(rememberScrollState())        ) {
movies.forEach { movie -> Card( elevation = CardDefaults.cardElevation( defaultElevation = 8.dp, ),
onClick = { onMovieClicked(movie) },
modifier = Modifier.padding(8.dp) ) { Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp) ) { Icon( imageVector = Icons.Default.Star, contentDescription = stringResource(id = R.string.movie) ) Display(text = movie.title) } } }
// ) } }}
CHANGED: /app/src/main/res/values/strings.xml
<resources> <string name="app_name">MovieUi1</string> <string name="movies">Movies</string> <string name="movie">Movie</string> <string name="title">Title</string> <string name="description">Description</string></resources>