Design systems are a collection of reusable components, guided by clear standards, that can be assembled together to build any number of applications with less time and effort.
In Android, we have the Material Design system. Material Design was created by Google in 2014, it offers a system for building bold, beautiful, and consistent digital products. It is a system that helps teams build high-quality digital experiences for Android, iOS, Flutter, and the web.
The current version of Material Design is Material Design 3 which was announced at Google I/O 2021. Material Design 3 is a major update to the Material Design system.
Material Design 3 has a host of different components that we can use to build our apps. This components have their equivalent Composables. Some of these components include:
- Buttons
- Cards
- Chips
- Dialogs
- TextFields
- etc.
We have already looked at a few of these components in the previous sessions. In this session, we will look at a few more components. All these components are availble by opting into the material library.
A card is a sheet of material that serves as an entry point to more detailed information. Cards may contain a photo, text, and a link about a single subject. They may display content containing elements of varying size, such as photos with captions of variable length.
In Jetpack Compose, we can use the Card Composable to create a card. The Card Composable is part of the material library.
Card is a surface with a background color and elevation. It can be used to display content in a contained space.
It has one Mandatory parameter:
-
onClick: This is used to specify the action to take when the card is clicked. It is of type() -> Unitand has no default value.
It has several optional parameters:
-
colors: This is used to specify the colors to use for the card. It is of typeCardColorsand has a default value ofCardDefaults.colors(). -
shape: This is used to specify the shape of the card. It is of typeShape. -
border: This is used to specify the border of the card. It is of typeBorderStroke. -
elevation: This is used to specify the elevation of the card. It is of typeElevation. -
content: This is used to specify the content of the card. It is of type@Composable ColumnScope.() -> Unitand will be called to draw the content of the card. All the items inside thecontentblock will be placed inside the card vertically since they will be in aColumnScope.
Create a Card
Let us wrap our search TextField in a Card Composable.
@ExperimentalMaterial3Api
@Composable
fun SearchBar() {
Card(
modifier = Modifier
.fillMaxWidth()
.height(75.dp)
.padding(end = 16.dp, start = 16.dp, top = 10.dp),
shape = RoundedCornerShape(100.dp),
) {
TextField(
modifier = Modifier.fillMaxSize(),
value = "",
onValueChange = { "" },
leadingIcon = { Image(painter = painterResource(id = R.drawable.ic_search), contentDescription = "search bar") },
trailingIcon = { Image(painter = painterResource(id = R.drawable.ic_filter), contentDescription = "filter") },
placeholder = {
Text(
text = "Search for food ...",
modifier = Modifier
.fillMaxHeight(),
textAlign = TextAlign.Center,
)
},
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
)
}
}
Floating Action Button
A floating action button (FAB) is a circular button that triggers the primary action in your app’s UI. This button floats above the content of the screen and usually resides on one corner of the screen.
In Jetpack Compose, we can use the FloatingActionButton Composable to create a floating action button.
The FloatingActionButton has one Mandatory parameter:
-
onClick: This is used to specify the action to take when the floating action button is clicked. It is of type() -> Unitand has no default value.
Several other optional arguments are available:
-
containerColor: This is used to specify the background color of the floating action button. It is of typeColorand has a default value ofMaterialTheme.colors.primary. -
contentColor: This is used to specify the color of the content of the floating action button. It is of typeColorand has a default value ofMaterialTheme.colors.onPrimary. -
elevation: This is used to specify the elevation of the floating action button. It is of typeElevationand has a default value ofElevation.Medium. -
shape: This is used to specify the shape of the floating action button. It is of typeShapeand has a default value ofCircleShape.
Add a Floating Action Button
Let us refactor our FoodItem Composable to use a FloatingActionButton instead of a Button.
@Composable
fun FoodItem(
food: Food,
id: Int = 0,
) {
Card(
modifier = Modifier
.width(200.dp)
.clickable {
}
.padding(end = 8.dp),
shape = RoundedCornerShape(corner = CornerSize(10.dp))) {
Column(modifier = Modifier
.padding(bottom = 5.dp)
.fillMaxWidth()) {
Image(
modifier = Modifier
.fillMaxWidth()
.height(150.dp),
painter = painterResource(id = food.banner),
contentDescription = "image",
contentScale = ContentScale.Crop
)
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(8.dp))
Column(
modifier = Modifier
.padding(horizontal = 5.dp)
.fillMaxWidth()) {
Text(
modifier = Modifier,
text = food.name,
style = MaterialTheme.typography.bodyLarge,
fontStyle = FontStyle.Normal,
fontWeight = FontWeight.ExtraBold)
Row(
modifier = Modifier
.padding(top = 5.dp),
verticalAlignment = Alignment.CenterVertically) {
Image(
painter = painterResource(id = R.drawable.ic_clock),
contentDescription = "Clock",
colorFilter = ColorFilter.tint(
color = MaterialTheme.colorScheme.onBackground
)
)
Text(
modifier = Modifier.padding(start = 3.dp),
text = "${food.waitTime} mins",
style = MaterialTheme.typography.bodySmall)
}
Spacer(modifier = Modifier
.height(2.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 4.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Text(
text = "$${food.originalPrice}",
fontWeight = FontWeight.ExtraBold,
style = MaterialTheme.typography.bodyLarge
)
FloatingActionButton(
onClick = { /*TODO*/ },
shape = CircleShape,
containerColor = MaterialTheme.colorScheme.tertiary) {
Image(
painter = painterResource(id = R.drawable.ic_add),
contentDescription = "Add",
colorFilter = ColorFilter.tint(
color = contentColorFor(MaterialTheme.colorScheme.tertiary)))
}
}
}
}
}
}
Scaffold Layout
The Scaffold Composable is a layout component that implements the basic material design visual layout structure. It is used to implement the top-level structure of a screen in your app.
The Scaffold Composable provides slots for the following:
-
App BarsApp Bars are used to display information and actions relating to the current screen. They can be placed at the top or bottom of the screen. TheScaffoldComposable provides a slot for the top app bar and the bottom app bar.-
topBar: This is used to specify the top app bar. It is of type@Composable () -> Unitand has no default value. -
bottomBar: This is used to specify the bottom app bar. It is of type@Composable () -> Unitand has no default value.
-
-
Floating Action ButtonFloating action buttons are used to trigger the primary action in your app’s UI. TheScaffoldComposable provides a slot for the floating action button.-
floatingActionButton: This is used to specify the floating action button. It is of type@Composable () -> Unitand has no default value.
-
-
contentThe content slot is used to specify the content of the screen. It is of type@Composable (PaddingValues) -> Unitand has no default value.The
PaddingValuesparameter is used to specify the padding to apply to the content. It is of typePaddingValuesand has a default value ofPaddingValues(0.dp). -
DrawerThe drawer slot is used to specify the drawer of the screen. It is of type@Composable () -> Unitand has no default value. -
Snackbars Snackbars are used to display brief messages to the user. The
ScaffoldComposable provides a slot for the snackbar.-
snackbarHost: This is used to specify the snackbar. It is of type@Composable () -> Unitand has no default value.
-
Add a Scaffold Layout
@Preview(showBackground = true)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = { ProfileBar() },
bottomBar = {
Text(text = "Bottom Bar")
}){ paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = paddingValues),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
){
Text(text = "Home Screen")
}
}
}
Conclusion
In this lesson, we have looked at how to implement a few other Material 3 components into our application. We have also got a better understanding of how the Scaffold Layout works.
In our next episode we will look at how to display a list of items on the screen using lazy layouts.