I want to create Cardview using jetpack compose but i am not able to find any
example.
You can use something like:
Card(
shape = RoundedCornerShape(8.dp),
backgroundColor = MaterialTheme.colors.surface,
) {
Column(
modifier = Modifier.height(200.dp).padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
){
Text(text = "This is a card view",
style = MaterialTheme.typography.h4)
}
}
With Material3 you can use:
Card(
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = /* ... */)
)
in v0.1.0-dev09 version, can be done like this, where the padding of Card sets the margin of the card, the padding of Box sets the padding inside the card.
Card(
shape = RoundedCornerShape(8.dp),
modifier = Modifier.padding(16.dp).fillMaxWidth()
) {
Box(modifier = Modifier.height(200.dp).padding(16.dp)) {
Text("This is a card view")
}
}
As the friends recommended, Card is a way of creating CardView but you can do that by surface too :
val shape = RoundedCornerShape(10.dp)
Surface(modifier = Modifier.size(250.dp, 70.dp), elevation = 8.dp, shape = shape) {
Text(text = "Sample text")
}
Note that Surface and Card cannot be used for positioning items so that if you wanna position that Text(text = "Sample text") , you should use one layout inside that Surface like this :
val shape = RoundedCornerShape(10.dp)
Surface(modifier = Modifier.size(250.dp, 70.dp), elevation = 8.dp, shape = shape) {
Box(modifier = Modifier.fillMaxSize()) {
Text(modifier = Modifier.align(Alignment.Center), text = "Sample text")
}
}
The appropriate way for creating CardView is this but if you wanna just create shadow for a view, you can use Modifier.shadow (Note that Modifier.shadow and Surface/Card are not the same):
Box(modifier = Modifier.size(250.dp, 70.dp).shadow(8.dp, RoundedCornerShape(10.dp)), contentAlignment = Alignment.Center) {
Text(text = "Sample Text")
}
Code:
class ImageCardActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val painter = painterResource(id = R.drawable.grogu)
val contentDescription = "Grogu says hi"
val title = "Force is strong with Grogu"
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier.fillMaxWidth(0.5f)
) {
ImageCard(
title = title,
contentDescription = contentDescription,
painter = painter
)
}
}
}
}
}
#Composable
fun ImageCard(
title: String,
contentDescription:String,
painter: Painter,
modifier:Modifier=Modifier
){
Card(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(18.dp),
elevation = 5.dp
) {
Box(
modifier = Modifier.height(200.dp)
) {
// Image
Image(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop
)
// Gradient
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent, Color.Black
),
startY = 300f
)
)
)
// Title
Box(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
contentAlignment = Alignment.BottomStart
) {
Text(
text = title,
style = TextStyle(color = Color.White, fontSize = 12.sp)
)
}
}
}
}
Related
I am new in jetpack compose, and I am try to learn it, so I have simple onboarding screen in jetpack compose, when I update project to jetpack compose 1.1.1 , "Next" button not working for onboarding screen to scroll horizontally. it was working when I use jetpack compose 1.0.0 version, I do not know what change in new version, any idea?
#ExperimentalPagerApi
#Composable
fun OnBoardScreen() {
val scaffoldState = rememberScaffoldState()
val onBoardViewModel : OnBoardViewModel = viewModel()
val context = LocalContext.current
val currentPage = onBoardViewModel.currentPage.collectAsState()
Toast.makeText(context, "${currentPage.value}", Toast.LENGTH_SHORT).show()
val pagerState = rememberPagerState(
pageCount = onBoardItem.size,
initialOffscreenLimit = 2,
initialPage = 0,
infiniteLoop = false
)
Scaffold(
modifier = Modifier.fillMaxSize(),
scaffoldState = scaffoldState
) {
Surface(
modifier = Modifier.fillMaxSize()
) {
LaunchedEffect(scaffoldState.snackbarHostState){
pagerState.animateScrollToPage(
page = currentPage.value
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.background(Gray200)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
HorizontalPager(
state = pagerState
) { page ->
Column(
modifier = Modifier
.padding(top = 65.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = onBoardItem[page].image),
contentDescription = "OnBoardImage",
modifier = Modifier
.size(250.dp)
)
Text(
text = onBoardItem[page].title,
modifier = Modifier
.padding(top = 50.dp),
color = Color.White,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
Text(
text = onBoardItem[page].desc,
modifier = Modifier
.padding(30.dp),
color = Color.White,
fontSize = 18.sp,
textAlign = TextAlign.Center
)
}
}
PagerIndicator(onBoardItem.size, pagerState.currentPage)
}
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
) {
Row(
modifier = Modifier
.padding(bottom = 20.dp)
.fillMaxWidth(),
horizontalArrangement = if (pagerState.currentPage != 2 ) {
Arrangement.SpaceBetween
} else {
Arrangement.Center
}
) {
if (pagerState.currentPage == 2) {
OutlinedButton(
onClick = {
Toast.makeText(context, "Start the Screen", Toast.LENGTH_SHORT).show()
},
shape = RoundedCornerShape(45.dp)
) {
Text(
text = "Get Started",
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 40.dp
),
color = Color.Black
)
}
} else {
Text(
text = "Skip",
color = Color.White,
modifier = Modifier.padding(start = 20.dp),
fontSize = 18.sp,
fontWeight = FontWeight.Medium
)
Text(
text = "Next",
color = Color.White,
modifier = Modifier
.clickable {
onBoardViewModel.setCurrentPage(pagerState.currentPage + 1)
}
.padding(end = 20.dp),
fontSize = 18.sp,
fontWeight = FontWeight.Medium
)
}
}
}
}
}
}
}
It looks like you expect these lines to scroll the pager every time you change the value of currentPage:
LaunchedEffect(scaffoldState.snackbarHostState) {
pagerState.animateScrollToPage(
page = currentPage.value
)
}
But scaffoldState.snackbarHostState in this scope is a static value, which means LaunchedEffect is not gonna be relaunched.
One option is to pass currentPage instead, but also, as your currentPage is backed by flow, it's cleaner to collect it:
LaunchedEffect(Unit) {
onBoardViewModel.currentPage
.collect {
pagerState.animateScrollToPage(
page = currentPage.value
)
}
}
p.s.
Also, when you need to make a text button, instead of adding clickable to the Text you can use TextButton:
TextButton(
onClick = {
}
) {
Text(/*...*/)
}
I wanna have this layout in Jetpack Compose. How can I place the image at the bottom of the card and have it a bit outside the card borders? Also how to make sure that the content above won't collide with the image, because the content above can be longer. Thanks very much.
A simple way is to use a Box and apply an Offset to the Image
Box() {
val overlayBoxHeight = 40.dp
Card(
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
) {
//...
}
Image(
painterResource(id = R.drawable.xxxx),
contentDescription = "",
modifier = Modifier
.align(BottomCenter)
.offset(x = 0.dp, y = overlayBoxHeight )
)
}
If you want to calculate the offset you can use a Layout composable.
Something like:
Layout( content = {
Card(
elevation = 10.dp,
modifier = Modifier
.layoutId("card")
.fillMaxWidth()
.height(300.dp)
) {
//...
}
Image(
painterResource(id = R.drawable.conero),
contentDescription = "",
modifier = Modifier
.layoutId("image")
)
}){ measurables, incomingConstraints ->
val constraints = incomingConstraints.copy(minWidth = 0, minHeight = 0)
val cardPlaceable =
measurables.find { it.layoutId == "card" }?.measure(constraints)
val imagePlaceable =
measurables.find { it.layoutId == "image" }?.measure(constraints)
//align the icon on the top/end edge
layout(width = widthOrZero(cardPlaceable),
height = heightOrZero(cardPlaceable)+ heightOrZero(imagePlaceable)/2){
cardPlaceable?.placeRelative(0, 0)
imagePlaceable?.placeRelative(widthOrZero(cardPlaceable)/2 - widthOrZero(imagePlaceable)/2,
heightOrZero(cardPlaceable) - heightOrZero(imagePlaceable)/2)
}
}
You can use Card for main view layout.
To have an icon placed like this, you need to place it with Card into a box, apply Alignment.BottomCenter alignment to image and add paddings.
To make image be under your text, I've added Spacer with size calculated on image size.
#Composable
fun TestView() {
CompositionLocalProvider(
LocalContentColor provides Color.White
) {
Column {
LazyRow {
items(10) {
Item()
}
}
Box(Modifier.fillMaxWidth().background(Color.DarkGray)) {
Text("next view")
}
}
}
}
#Composable
fun Item() {
Box(
Modifier
.padding(10.dp)
) {
val imagePainter = painterResource(id = R.drawable.test2)
val imageOffset = 20.dp
Card(
shape = RoundedCornerShape(0.dp),
backgroundColor = Color.DarkGray,
modifier = Modifier
.padding(bottom = imageOffset)
.width(200.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(20.dp)
) {
Text(
"Title",
modifier = Modifier.align(Alignment.Start)
)
Spacer(modifier = Modifier.size(15.dp))
Text("PM2.5")
Text(
"10",
fontSize = 35.sp,
)
Text("m/s")
Spacer(modifier = Modifier.size(15.dp))
Text(
"Very Good",
fontSize = 25.sp,
)
Spacer(
modifier = Modifier
.size(
with(LocalDensity.current) { imagePainter.intrinsicSize.height.toDp() - imageOffset }
)
)
}
}
Image(
painter = imagePainter,
contentDescription = "",
Modifier
.align(Alignment.BottomCenter)
)
}
}
Result:
I got into Jetpack compose and I am having a hard time finding examples of a normal screen. Everyone just uses scaffold and calls it a day. Using fillMaxSize is supposed to do exactly this, can someone explain how composable inherit their size? The topmost composable specifically (root composable)?
#Composable
fun MainContent() {
Surface(modifier = Modifier.fillMaxSize()) {
Column() {
SearchBox()
CurrentWeather()
WindAndHumidityBox()
}
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeweathertestTheme {
// A surface container using the 'background' color from the theme
Surface(
color = MaterialTheme.colors.background,
modifier = Modifier.fillMaxSize()
) {
MainContent()
}
}
}
}
}
Also the PURPLE portion is using this code.
Surface(
modifier = Modifier
.padding(40.dp).wrapContentHeight(), elevation = 3.dp, shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colors.primarySurface
) {
In XML we could use wrap content and apply padding to it, this doesn't seem to be the case with compose? I am trying to do something like this
EDIT: Whole compose file
#Composable
private fun Content() {
Column(modifier = Modifier.fillMaxSize()) {
SearchBox()
CurrentWeather()
WindAndHumidityBox()
}
}
#Composable
fun WindAndHumidityBox() {
Surface(
modifier = Modifier
.padding(40.dp)
.wrapContentHeight(), elevation = 3.dp, shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colors.primarySurface
) {
Row {
Row(modifier = Modifier.align(Alignment.CenterVertically)) {
Image(
painter = painterResource(id = R.drawable.day_clear),
contentDescription = "humidity"
)
Text(
text = "Precipitation \n 10%", fontWeight = FontWeight.Bold, fontSize = 20.sp,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
Row(modifier = Modifier.align(Alignment.CenterVertically)) {
Image(
painter = painterResource(id = R.drawable.day_clear),
contentDescription = "humidity"
)
Text(
text = "Precipitation \n 10%", fontWeight = FontWeight.Bold, fontSize = 20.sp,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
}
}
}
#Composable
private fun CurrentWeather() {
Box(modifier = Modifier.fillMaxWidth()) {
Column(Modifier.align(Alignment.Center)) {
Column(modifier = Modifier.padding(bottom = 20.dp)) { // location and time/date
Row(modifier = Modifier.align(Alignment.CenterHorizontally)) {
Icon(
painter = painterResource(id = R.drawable.ic_location),
contentDescription = "location"
)
Text(
text = "London", fontWeight = FontWeight.Bold, fontSize = 20.sp,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
Text(text = "Thu 4 December 8:41 am")
}
Row(Modifier.align(Alignment.CenterHorizontally)) {
Image(
painterResource(id = R.drawable.day_clear),
modifier = Modifier.align(alignment = Alignment.CenterVertically),
contentDescription = "current weather image displaying the current weather"
)
Text(
text = "27", fontSize = 40.sp, modifier = Modifier
.padding(5.dp)
.align(Alignment.Top)
)
}
Text(
text = "Sunny",
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(top = 20.dp),
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
}
}
}
#Composable
private fun SearchBox() {
Box {
var textValue by remember { mutableStateOf("") }
val context = LocalContext.current
OutlinedTextField(value = textValue,
leadingIcon = {
Icon(
painterResource(id = R.drawable.ic_baseline_search_24),
"search"
)
},
label = { Text("Search For A City") },
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search,
keyboardType = KeyboardType.Text
),
keyboardActions = KeyboardActions(
onSearch = {
hideKeyboard(context = context)
}),
onValueChange = { text ->
textValue = text
}
)
}
}
You are using too many parent containers.
Just use:
Surface(
color = MaterialTheme.colors.background,
modifier = Modifier.fillMaxSize()
){
Column(){
WindAndHumidityBox()
}
}
Then you can achieve the PURPLE portion in many ways.
In particular check the Intrinsic measurements.
For example:
Surface(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
elevation = 3.dp,
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colors.primarySurface
) {
Row(
Modifier.padding(40.dp), //Padding inside the Row
horizontalArrangement = Arrangement.Center
) {
Row(modifier = Modifier
.align(Alignment.CenterVertically)
.height(IntrinsicSize.Min) //Intrinsic measurements to center the icon vertically
) {
Image(
modifier = Modifier.fillMaxHeight(),
//..
)
//...
}
//...
}
}
How do I create a rounded checkbox in Jetpackcompose like this. I tried using a Shape composable on it but it doesn't work.
I was looking on how to do the same thing you were asking, your question helped me on my journey so it is only fair I share. Add some animations and you are set my friend.
Make a round looking icon by using a box and an icon
Box(
modifier = Modifier
.clip(CircleShape)
.size(40.dp)
.background(Color.Black)
.padding(3.dp)
.clip(CircleShape)
.background(Color.White),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
2.Place the newly made rounded icon and some text next to each other by using a Row
Row(
verticalAlignment = Alignment.CenterVertically,
){
Box(
modifier = Modifier
.clip(CircleShape)
.size(40.dp)
.background(Color.Black)
.padding(3.dp)
.clip(CircleShape)
.background(Color.White),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
3.Replace whatever you want with variables so you can customize
it
#Composable
fun RoundedCheckView(
) {
val isChecked = remember { mutableStateOf(false) }
val checkedText = remember { mutableStateOf("unChecked") }
val circleSize = remember { mutableStateOf(20.dp) }
val circleThickness = remember { mutableStateOf(2.dp) }
val color = remember { mutableStateOf(Color.Gray) }
Row(
verticalAlignment = Alignment.CenterVertically,
{
Box(
modifier = Modifier
.clip(CircleShape)
.size(circleSize.value)
.background(color.value)
.padding(circleThickness.value)
.clip(CircleShape)
.background(Color.White) ,
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
}
4.Finally add Modifier.toggleable to the row, basically making it a clickable item that toggles (between true and false) a variable in this case isChecked. Then just customize the variables according to what you need
#Composable
fun RoundedCheckView()
{
val isChecked = remember { mutableStateOf(false) }
val checkedText = remember { mutableStateOf("unChecked") }
val circleSize = remember { mutableStateOf(20.dp) }
val circleThickness = remember { mutableStateOf(2.dp) }
val color = remember { mutableStateOf(Color.Gray) }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.toggleable(value = isChecked.value,role = Role.Checkbox) {
isChecked.value = it
if (isChecked.value) {
checkedText.value = "Checked"
circleSize.value = 40.dp
circleThickness.value = 3.dp
color.value = Color.Black
} else {
checkedText.value = "unChecked"
circleSize.value = 20.dp
circleThickness.value = 2.dp
color.value = Color.Gray
}
}) {
Box(
modifier = Modifier
.clip(CircleShape)
.size(circleSize.value)
.background(color.value)
.padding(circleThickness.value)
.clip(CircleShape)
.background(Color.White) ,
contentAlignment = Alignment.Center
) {
if(isChecked.value){
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
}
This is how we can make a custom check box in jetpack compose
val isCheck = remember { mutableStateOf(false) }
Row {
Card(
modifier = Modifier.background(Color.White),
elevation = 0.dp,
shape = RoundedCornerShape(6.dp),
border = BorderStroke(1.5.dp, color = titleColor)
) {
Box(
modifier = Modifier
.size(25.dp)
.background(if (isCheck.value) titleColor else Color.White)
.clickable {
isCheck.value = !isCheck.value
},
contentAlignment = Center
) {
if(isCheck.value)
Icon(Icons.Default.Check, contentDescription = "", tint = Color.White)
}
}
Text(
modifier = Modifier
.align(CenterVertically)
.padding(start = 10.dp),
text = "I agree with the terms & condition",
)
}
You can try to make it use Box with modifier content alignment center. and put an icon on there.
#Preview
#Composable
fun Check() {
Box(
modifier = Modifier
.clip(CircleShape)
.size(50.dp)
.background(Color.Red)
.padding(5.dp)
.clip(CircleShape)
.background(Color.Blue),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
}
We can use animations to make it behave similar to the default Checkbox. Using Modifier.toggleable on the top-level Row, the entire thing is clickable, including the label. This also creates the proper semantics for screen reader users. You can change the shape of the card to get a circular checkbox.
#Composable
fun PrimaryCheckbox(
label: String,
modifier: Modifier = Modifier,
size: Float = 24f,
checkedColor: Color = DarkGray,
uncheckedColor: Color = White,
checkmarkColor: Color = White,
onValueChange: () -> Unit
) {
var isChecked by remember { mutableStateOf(false) }
val checkboxColor: Color by animateColorAsState(if (isChecked) checkedColor else uncheckedColor)
val density = LocalDensity.current
val duration = 200
Row(
modifier = modifier
.toggleable(
value = isChecked,
role = Role.Checkbox,
onValueChange = {
isChecked = !isChecked
onValueChange.invoke()
}
)
) {
Card(
elevation = 0.dp,
shape = RoundedCornerShape(4.dp),
border = BorderStroke(1.5.dp, color = checkedColor),
) {
Box(
modifier = Modifier
.size(size.dp)
.background(checkboxColor),
contentAlignment = Alignment.Center
) {
androidx.compose.animation.AnimatedVisibility(
visible = isChecked,
enter = slideInHorizontally(
animationSpec = tween(duration)
) {
with(density) { (size * -0.5).dp.roundToPx() }
} + expandHorizontally(
expandFrom = Alignment.Start,
animationSpec = tween(duration)
),
exit = fadeOut()
) {
Icon(
Icons.Default.Check,
contentDescription = null,
tint = checkmarkColor
)
}
}
}
Text(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 8.dp),
text = label,
)
}
}
I want to make an Image with Text on top, just like Google CLassroom. But first I want to test Image and then Text. Instead, I got the image overlapping the text. Image Overlapping text
Then I move the Image code after the text. How to get simple G classroom format
Text then Image
#Composable
fun ClassImage(
// icon: VectorAsset,
// label: String,
// modifier: Modifier = Modifier
) {
val imageAlpha = 1f
Surface(
modifier = Modifier
.padding(start = 8.dp, top = 8.dp, end = 8.dp)
.fillMaxWidth(),
color = colors.primary.copy(alpha = 0.12f)
) {
TextButton(
onClick = {},
modifier = Modifier.fillMaxWidth()
) {
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth())
{
Image(
imageResource(id = R.drawable.class1),
alpha = imageAlpha
)
Column {
Text("Alfred Sisley", fontWeight = FontWeight.Bold)
ProvideEmphasis(emphasis = EmphasisAmbient.current.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
}
}
}
}
}
To put Text on top of the Image you can use Box, which is similar to old FrameLayout.
I'm not sure want you wanted to achieve, but if smth like this:
Then you can do it this way:
Surface(
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.preferredHeight(128.dp)
.clickable(onClick = {})
) {
Box {
Image(
vectorResource(id = R.drawable.ic_launcher_background),
alpha = imageAlpha,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(modifier = Modifier.padding(16.dp)) {
Text(
"Alfred Sisley",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.h6)
ProvideEmphasis(emphasis = EmphasisAmbient.current.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
Spacer(modifier = Modifier.weight(1f))
Text(text = "Footer", style = MaterialTheme.typography.body1)
}
}
}
With 1.0.0-beta02 you can use a Box as parent container.
Something like:
Box(modifier = Modifier.height(IntrinsicSize.Max))
{
Image(
painterResource(id = R.drawable.xx),
"contentDescription",
alpha = 0.8f,
modifier = Modifier.requiredHeight(100.dp)
)
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Bottom) {
Text("Alfred Sisley",
fontWeight = FontWeight.Bold)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
}
}