Jetpack Compose — Chapter 13: Using a Scaffold Layout

Jetpack Compose — Chapter 13: Using a Scaffold Layout

Introduction

Hi, code wizards!!!

When building applications, a design system can be your best friend. This is a pre-prepared collection of reusable elements, all guided by clear standards, which can be brought together to speedily construct any number of apps, saving time and effort.

So when I started to study scaffolding, I wasn't sure why I just had to use it. That will be another topic — let’s focus on how to use it in this article.

As I mentioned, form some time I will try to include one YouTube video, including relevant info/tutorials on live coding. There are many of them, but let’s try to choose one. Watch the movie, and check the code in that article to move forward!

Our Enchanted Journey Through Jetpack Compose

Before we start, please look at where we are in our chapter list so you can explore what more that collection of articles will give you.

A Little Bit of History

Unfortunately, this time, we must speak about the past; it will be hard to understand why we need a Scaffold.

In the realm of Android, we have an incredible resource in the Material Design system.

Google launched Material Design in 2014, and it’s been a game changer ever since.

It provides us with a framework to create stunning, bold, and consistent digital products. The system is not confined only to Android; it can also assist in building high-quality digital experiences for iOS, Flutter, and the web.

Material Design is now upgraded to its third iteration, a significant update to the system announced at Google I/O 2021.

This version is loaded with several components that are indispensable when it comes to building our apps. These components have equivalent Composables, which include:

  • Buttons
  • Cards
  • Chips
  • Dialogs
  • TextFields
  • and more…

I’ve touched upon a few in the past, and today I will be introducing some more. We can access all these components by opting for the material library.

Jetpack Compose — Chapter 9: Simple Guide to Adding a TextField

A straightforward guide to seamlessly integrating TextFields into your Jetpack Compose UI

medium.com

Card

Let me start with a card, a vital component that acts as an entry point to detailed information.

Cards can host a mix of photos, text, and links related to a single subject, accommodating different-sized content. This is where the Card Composable in the material library comes in handy.

The Card in Jetpack Compose is essentially a surface colored background with elevation, meant to display content. Its key parameters include:

  • onClick: This mandatory parameter defines the action upon clicking the card. Its type is () -> Unit, and it doesn't have a default value.
  • colors: This optional parameter is used to determine the colors of the card, its type is CardColors and has CardDefaults.colors() as the default value.
  • shape: This outlines the shape of the card, this is of type Shape.
  • border: Helps in defining the border of the card, its type is BorderStroke.
  • elevation: It's used to define the elevation of the card, and it is of type Elevation.
  • content: It specifies the content of the card. Of type @Composable ColumnScope.() -> Unit, this parameter is activated to design the card's content. Moreover, all the items within the content parameter are vertically aligned in the card by being placed in a ColumnScope.

Alright, let’s proceed to encapsulate our TextField for search within an elegant Card Composable.

@ExperimentalMaterial3Api
@Composable
fun ArcaneSearchOrb() {
    // The mystical card that harbors our spellbound search abilities
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .height(75.dp) // The height of our orb, contained yet powerful
            .padding(horizontal = 16.dp, vertical = 10.dp), // Ensconced within ethereal padding
        shape = RoundedCornerShape(50.dp), // A perfectly rounded orb, smooth and inviting
    ) {
        // The core of our orb, where the magic of searching begins
        TextField(
            modifier = Modifier.fillMaxSize(), // The TextField spans the entire orb
            value = "", // The incantation to summon the search terms remains unspoken
            onValueChange = { /* The magic reacts, changing with each keystroke */ },
            leadingIcon = { 
                // The icon that calls forth the vision, a magnifying glass to peer into the depths
                Image(painter = painterResource(id = R.drawable.ic_magnify), contentDescription = "Scry for items")
            },
            trailingIcon = { 
                // The icon that conjures filters, allowing the seeker to refine their quest
                Image(painter = painterResource(id = R.drawable.ic_spell_filter), contentDescription = "Conjure filters")
            },
            placeholder = {
                // The guiding text, a whisper of encouragement to those who seek
                Text(
                    text = "Scry for arcane ingredients...",
                    modifier = Modifier.fillMaxHeight(),
                    textAlign = TextAlign.Center, // Centered, as all things should be
                )
            },
            colors = TextFieldDefaults.textFieldColors(
                focusedIndicatorColor = Color.Transparent, // Invisible bounds, for the focus is within
                unfocusedIndicatorColor = Color.Transparent, // Unseen, as the orb's magic is self-contained
            ),
        )
    }
}        
Think of a floating action button (FAB) as an interactive circular button that triggers the primary action within your app’s UI.

The feature of this button is that it hovers above the interface displaying content, typically situated at a screen corner.

FloatingActionButton

Jetpack Compose facilitates the creation of a floating action button using the FloatingActionButton Composable.

The FloatingActionButton insists on one mandatory parameter:

  • onClick: It defines the action that unfolds once the floating action button is clicked. The parameter type is () -> Unit, with no set default value.

Moreover, there are a host of optional arguments that you can add:

  • containerColor: Set the background color of the floating action button. The default value is MaterialTheme.colors.primary.
  • contentColor: Choose the color for the content within the floating action button, the default being MaterialTheme.colors.onPrimary.
  • elevation: Define the elevation for the floating action button. The default default value is Elevation.Medium.
  • shape: Determine the shape of the floating action button, with the default being CircleShape.

Let’s revamp our EnchantedMorsel Composable to incorporate a FloatingActionButton in lieu of a Button.

@Composable
fun EnchantedMorsel(
    morsel: Morsel, // Our mystical food entity, brimming with properties
    enchantmentId: Int = 0, // An optional enchantment to bestow unique powers
) {
    Card(
        modifier = Modifier
            .width(200.dp) // The width of our magical tome
            .clickable { /* Invoke a spell upon touch */ }
            .padding(end = 8.dp), // Ensuring the tome sits perfectly on the mystical shelf
        shape = RoundedCornerShape(corner = CornerSize(10.dp)) // Softened edges to avoid paper cuts from the ether
    ) {
        Column(modifier = Modifier
            .padding(bottom = 5.dp)
            .fillMaxWidth()) { // The structure of our spell
            Image(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(150.dp), // The visual representation of our morsel
                painter = painterResource(id = morsel.image),
                contentDescription = "Morsel's visage",
                contentScale = ContentScale.Crop // Ensuring the image is as enticing as the morsel itself
            )
            Spacer(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(8.dp)) // A breath between the visual and the details
            Column(
                modifier = Modifier
                    .padding(horizontal = 5.dp)
                    .fillMaxWidth()) { // Where the details of the morsel are unveiled
                Text(
                    modifier = Modifier,
                    text = morsel.name, // The name of our delectable morsel
                    style = MaterialTheme.typography.subtitle1,
                    fontWeight = FontWeight.Bold) // Bold, as bold as the flavors
                
                Row(
                    modifier = Modifier
                        .padding(top = 5.dp),
                    verticalAlignment = Alignment.CenterVertically) { // A row for time, as even in magic, preparation takes a moment
                        
                    Image(
                        painter = painterResource(id = R.drawable.ic_time),
                        contentDescription = "Preparation Time",
                        colorFilter = ColorFilter.tint(
                            color = MaterialTheme.colors.onSecondary
                        ) // Ensuring the icon matches the theme of the tome
                    )
                    Text(
                        modifier = Modifier.padding(start = 3.dp),
                        text = "${morsel.preparationTime} mins", // The time it takes to conjure this morsel into existence
                        style = MaterialTheme.typography.body2)
                }
                Spacer(modifier = Modifier.height(2.dp)) // A slight pause before the final revelation
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(horizontal = 4.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically) { // A row for price and the magical addition button
                    Text(
                        text = "$${morsel.price}", // The cost, in gold or gems, for this morsel
                        fontWeight = FontWeight.Bold,
                        style = MaterialTheme.typography.subtitle1
                    )
                    FloatingActionButton(
                        onClick = { /* A spell to add the morsel to your feast */ },
                        shape = CircleShape,
                        containerColor = MaterialTheme.colors.secondary) { // A button, as round as a cauldron, to add the morsel
                        Image(
                            painter = painterResource(id = R.drawable.ic_plus),
                            contentDescription = "Add to Feast",
                            colorFilter = ColorFilter.tint(
                                color = contentColorFor(MaterialTheme.colors.secondary))) // The plus sign, the universal symbol of addition
                    }
                }
            }
        }
    }
}        

Scaffold

Finally, the Scaffold Composable is an essential layout component that materializes the basic design and structure of the material interface. It's the go-to layout component for constructing the broader structure of a screen in your app.

The Scaffold Composable provides placeholders for the following components:

App Bars App Bars, which showcase essential data and operations relative to the active screen, sit comfortably at the top or bottom. The Scaffold Composable earmarks are placeholders for both the top and bottom app bars.

  • topBar: This role is to define the top app bar. Its type is @Composable () -> Unit, and it doesn't have a preset value.
  • bottomBar: Used to define the bottom app bar with the type @Composable () -> Unit without a default value.

Floating Action Button Floating action buttons prompt the primary action in your UI and the Scaffold Composable provides a spot for it.

  • floatingActionButton: This is used to designate the floating action button with the type @Composable () -> Unit with no preset value.

content With the content slot, you can define the main content of the screen. It is of type @Composable (PaddingValues) -> Unit and isn't preconfigured.

The PaddingValues parameter enables you to set padding for the content. Its type is PaddingValues and defaults to PaddingValues(0.dp).

Drawer The drawer slot lets you specify the screen's drawer. It is of type @Composable () -> Unit without a preset value.

  • snackbarHost: This is employed to define the snackbar. Its type is @Composable () -> Unit with no stock value.

@Preview(showBackground = true)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen() {
  Scaffold(
      modifier = Modifier.fillMaxSize(),
      topBar = { UserBar() },
      bottomBar = {
        Text(text = "Lower Bar")
      }){ paddingValues ->
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(paddingValues = paddingValues),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
            ){
              Text(text = "Welcome!")
            }
  }
}        

In this segment, we’ve tackled the execution of a few additional Material 3 components in our application, thereby gaining a deeper understanding of the Scaffold Layout’s operation.

It wasn’t so difficult — wasn’t it?

Let’s prepare for our subsequent part, where we’ll explore the world of displaying a sequence of items on the screen with lazy layouts.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了