Material 3 UI Components in Jetpack Compose
Material Components
Jetpack Compose offers Material components that are composed of basic UI elements. These components encapsulate specific features, functionalities, or parts of your app's user interface. Using UI components promotes reusability, maintainability, and consistency in your app's design.
Components in Material Compose
Card
Scaffold
Button
App Bars
Floating Action Button
Chip
Dialog
Progress Indicators
Slider
Switch
Bottom Sheets
Navigation Drawer
Snackbar
Lists and Grids
We will be tackling each and every component one article at a time, delving into detail on what each component entails and all the various types, within the component.
For this Article, we are going to have a look at the Material Card Component.
We will design a Profile Card component using the Card
Composable, look at several implementations using key component parameters for Card customization, and look at another example of a customized profile card using the Surface
Composable.
Below is an example of what we will do.
Card
The Card composable serves as a Material Design container for your user interface. Typically, cards contain a single coherent piece of content. Here are several scenarios in which you might use a card:
A profile Card displaying your profile details
A product displayed in a shopping app.
A news item from a news app.
A message in a messaging app eg an error message.
Here are some key components of a Card component that allow you to customize the appearance and behavior of the component.:
@Composable
fun Card(
// add onclick functionality to your card
onClick: () -> Unit,
//the Modifier to be applied to this card
modifier: Modifier = Modifier,
//controls the enabled state of this card
enabled: Boolean = true,
//shape defines the shape of this card's container
shape: Shape = CardDefaults.shape,
//colors defines CardColors that will be used to resolve the colors used for this card in different states
colors: CardColors = CardDefaults.cardColors(),
//elevation defines CardElevation used to resolve the elevation for this card in different states.
elevation: CardElevation = CardDefaults.cardElevation(),
//defines the border to draw around the container of this card
border: BorderStroke? = null,
//define the content for your card
content: @Composable ColumnScope.() -> Unit
): Unit
The Profile Card
A Profile Card typically displays a user's profile information in a visually appealing way. It commonly consists of the following UI elements:
Profile Image: A user's profile picture.
Name: The user's name or title.
Occupation: The user's occupation.
Description: A brief description.
To create the Profile Card UI component, we combine the Profile Image, Name, Occupation, and Description within a Surface
composable. The Surface
provides elevation (a drop shadow), rounded corners, and background color to give the card a visually appealing appearance.
First, we will create a data class to help store, manipulate, and manage the user data, let's call this data class, UserDomain.
data class UserDomain(
//get the image from the inline link above `Profile Image` and save as profile
val profileImageUrl: Int = R.drawable.profile,
val name: String = "Name",
val occupation: String,
val description: String
)
Filled Card
The following is an example of how to use a filled card. The trick here is to modify the filled color using the colors
attribute.
@Composable
fun FilledProfileCard(
userData: UserDomain
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant,
),
) {
Column(
modifier =
Modifier.padding(16.dp)
) {
Image(
painter = painterResource(id = userData.profileImageUrl),
contentDescription = "Profile Image",
modifier = Modifier
.size(120.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(16.dp))
// Name
Text(
text = userData.name,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
)
Spacer(modifier = Modifier.height(4.dp))
// Occupation
Text(
text = userData.occupation,
style = TextStyle(
fontSize = 16.sp,
color = Color.Gray
)
)
Spacer(modifier = Modifier.height(8.dp))
// Description
Text(
text = userData.description,
style = TextStyle(
fontSize = 14.sp
)
)
}
}
}
@Preview(showBackground = true)
@Composable
fun FilledProfileCardPreview() {
FilledProfileCard(
userData = UserDomain(
occupation = "Android Engineer",
description = "This is a profile card, used to help explain UI Components in Compose"
)
)
}
Elevated Card
The code below explains how to use an Elevated card. Make use of the ElevatedCard
composable.
The elevation
attribute can be used to modify the appearance of elevation and the resulting shadow.
@Composable
fun ElevatedProfileCard(
userData: UserDomain
) {
ElevatedCard(
elevation = CardDefaults.cardElevation(
defaultElevation = 10.dp
),
modifier = Modifier
.padding(16.dp)
) {
Column(
modifier =
Modifier.padding(16.dp)
) {
Image(
painter = painterResource(id = userData.profileImageUrl),
contentDescription = "Profile Image",
modifier = Modifier
.size(120.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(16.dp))
// Name
Text(
text = userData.name,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
)
Spacer(modifier = Modifier.height(4.dp))
// Occupation
Text(
text = userData.occupation,
style = TextStyle(
fontSize = 16.sp,
color = Color.Gray
)
)
Spacer(modifier = Modifier.height(8.dp))
// Description
Text(
text = userData.description,
style = TextStyle(
fontSize = 14.sp
)
)
}
}
}
@Preview(showBackground = true)
@Composable
fun ElevatedProfileCardPreview() {
ElevatedProfileCard(
userData = UserDomain(
occupation = "Android Engineer",
description = "This is a profile card, used to help explain UI Components in Compose"
)
)
}
Outlined Card
An example of an outlined card is shown below. Make use of the OutlinedCard
composable.
@Composable
fun OutlinedProfileCard(
userData: UserDomain
) {
OutlinedCard(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface,
),
border = BorderStroke(1.dp, Color.Black),
modifier = Modifier
.padding(16.dp)
) {
Column(
modifier =
Modifier.padding(16.dp)
) {
Image(
painter = painterResource(id = userData.profileImageUrl),
contentDescription = "Profile Image",
modifier = Modifier
.size(120.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(16.dp))
// Name
Text(
text = userData.name,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
)
Spacer(modifier = Modifier.height(4.dp))
// Occupation
Text(
text = userData.occupation,
style = TextStyle(
fontSize = 16.sp,
color = Color.Gray
)
)
Spacer(modifier = Modifier.height(8.dp))
// Description
Text(
text = userData.description,
style = TextStyle(
fontSize = 14.sp
)
)
}
}
}
@Preview(showBackground = true)
@Composable
fun OutlinedProfileCardPreview() {
OutlinedProfileCard(
userData = UserDomain(
occupation = "Android Engineer",
description = "This is a profile card, used to help explain UI Components in Compose"
)
)
}
The Customized Profile Card UI Component
Let's delve into our example, the Customized Profile Card UI component.
@Composable
fun ProfileCard(
userData: UserDomain
) {
// Surface composable creates a card-like container
Surface(
modifier = Modifier
.fillMaxWidth() //used to make the card span the full width of its parent
.padding(16.dp), //add spacing around the card
shadowElevation = 4.dp,
shape = RoundedCornerShape(8.dp), //give rounded shaped corners
color = Color.White
) {
Column(
modifier = Modifier
.padding(16.dp)
) {
// Profile Image
Image(
painter = painterResource(id = userData.profileImageUrl),
contentDescription = "Profile Image",
modifier = Modifier
.size(120.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(16.dp))
// Name
Text(
text = userData.name,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
)
Spacer(modifier = Modifier.height(4.dp))
// Occupation
Text(
text = userData.occupation,
style = TextStyle(
fontSize = 16.sp,
color = Color.Gray
)
)
Spacer(modifier = Modifier.height(8.dp))
// Description
Text(
text = userData.description,
style = TextStyle(
fontSize = 14.sp
)
)
}
}
Limitations
Cards
do not have their own scroll or dismiss actions, but they can be integrated into composables that do. To implement swipe to dismiss on a card, for example, use the SwipeToDismiss
composable. Use scroll modifiers like, verticalScroll
to integrate with scroll. More information can be found in the Scroll documentation.
Recommended Reads:
Subscribe to my newsletter
Read articles from Michelle and Jeremy directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by