I have a Row that contains a circle Image. A Column with 2 BasicText in there and a Spacer between Column and Image. I want to have a way to "highlight" this row by giving it a blue tint with some opacity overlay. The closest I can get is by giving the Row a background color, but it doesn't give all the children blue-ish tint. I know background will not work because the color isn't drawn overlay but instead behind all other elements. Is there a way to achieve this? Thanks!
The best way I can think of is the following:
YourActivityTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.wrapContentSize(),
color = MaterialTheme.colors.background
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
.padding(8.dp),
shape = RoundedCornerShape(12.dp),
border = BorderStroke(2.dp, Color.Black)
) {
Row(Modifier.padding(8.dp)) {
Surface(
modifier = Modifier.size(50.dp),
shape = CircleShape,
border = BorderStroke(2.dp, Color.Black),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.2f)
) {
// Image goes here
}
Column(
modifier = Modifier
.padding(start = 8.dp)
.align(Alignment.CenterVertically)
) {
Text("Text1", fontWeight = FontWeight.Bold)
Text("Text2", style = MaterialTheme.typography.body2)
}
}
Box(modifier = Modifier
.fillMaxSize()
.alpha(0.2f)
.background(Color.Blue))
}
}
}
There are endless ways you can create your example. That one is the best that comes to my mind. If you have any question please fill free to ask me!
Related
Hey guys I am using RoundedCornerShape(4.dp) to my Surface which looks fine. When I tried to click on the item it not showing me 4dp corner in Surface. I tried this stack overflow 1 and stack overflow 2 but nothing works.
binding.itemComposable.setContent {
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
val options = getOptions()
options.forEachIndexed { _, optionText ->
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val backgroundColor = if (isPressed) DuckEggBlue else OffWhite
val textColor = if (isPressed) TealBlue else Slate
val borderWidth = if (isPressed) 1.dp else 0.dp
val borderColor = if (isPressed) Aqua else OffWhite
val clickable = Modifier.clickable(
interactionSource = interactionSource,
indication = rememberRipple(true)
) {
println("Item Click")
}
Surface(
modifier = Modifier
.then(clickable)
.border(borderWidth, borderColor),
shape = RoundedCornerShape(4.dp)
) {
Text(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor)
.padding(16.dp),
text = optionText,
style = Typography.h3,
fontWeight = FontWeight.Medium,
color = textColor
)
}
}
}
}
Without click on item corner is 4 dp
When I click it's not changing corner
If you want to handle the click on a Surface you have to use the function that accepts an onClick():
Surface(
onClick = {},
shape = RoundedCornerShape(4.dp),
border = BorderStroke(borderWidth,borderColor),
interactionSource = interactionSource
)
Create a variable for shape
val shape = RoundedCornerShape(4.dp)
Use it in Modifier.clip() and Modifier.border() like this,
Surface(
modifier = Modifier
.clip(shape)
.border(
width = borderWidth,
color = borderColor,
shape = shape,
)
.then(clickable),
// shape = shape,
)
shape in border() specifies the shape of the border which by default is RectangleShape. Hence, you are seeing the rectangle border.
shape in clip() changes the shape of the composable before the click action is added. This is to make the ripple effect appear only on the given shape.
Note: Order of modifiers are important.
The shape in the Surface may not be needed after these changes.
If youre using Surface to wrapping the content, try to add a container inside the content for example Box or Column. Then use your Surface only as a shape mask, the background and other content will be flexible as you want.
This is the example
Surface(
modifier = Modifier
.then(clickable)
.border(borderWidth, borderColor),
shape = RoundedCornerShape(4.dp)
) {
Box(modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(Color.Green)){
Text(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
text = optionText,
style = Typography.h3,
fontWeight = FontWeight.Medium,
color = textColor
)
}
}
I'am new to jetpack compose and i really liked it. But ran into a problem : I want to create a card and inside of it, i would like to display a list of item with divider between them. I was almost able to achieved it here is my code :
Box(modifier = Modifier.background(Color(0xFFf4f4f4))
.fillMaxSize()
.padding(top = 20.dp)) {
Card(
modifier = Modifier
.fillMaxWidth(),
shape = RoundedCornerShape(20.dp),
elevation = 5.dp
) {
val colorNamesList = listOf("Red", "Green", "Blue", "Indigo")
LazyColumn() {
itemsIndexed(
colorNamesList,
) { index, item ->
Column(
modifier = Modifier
.background(
color = Color(0xFFf2f2f2),
)
.clickable {
println(item)
},
horizontalAlignment = Alignment.CenterHorizontally,//those 2 does nothing
verticalArrangement = Arrangement.Center //when i change it nothing change
) {
println(item + index)
Text(
text = "Item at $item",
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
//textAlign = TextAlign.Center, without it image 1, with it image 2
color = Color.Black,
)
if (index < colorNamesList.lastIndex) {
Divider(
color = Color.Black.copy(alpha = 0.2f),
modifier = Modifier
.padding(horizontal = 80.dp),
)
}
}
}
}
}
}
It's almost good but i didn't managed to align the text inside it, i searched some answer but Alignment and Arrangement doesn't change anything in my situation.
I was able to align horizontally by adding textAlign = TextAlign.Center as i commented in my code but, i couldn't align vertically, the text stay on the top of his cell :
What i want to do is text center in every cell of the column. Thanks for the help.
You can achieve this by adding similar xml attribute like "wrap_content" as height in compose too.
Add "wrapContentHeight()" to modifier and you will get it to align to centre of the page.
Text(
text = "Item at $item",
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.wrapContentHeight(),
textAlign = TextAlign.Center
color = Color.Black,
)
Lets say i have two screnario have divider's that get padding from top.
First scenario (Aligment.TopCenter);
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.TopCenter
) {
Divider(
thickness = 3.dp,
color = Color.Red)
Divider(
modifier = Modifier.padding(top = 200.dp),
thickness = 3.dp,
color = Color.Black)
}
Screen:
Second scenario (Aligment.Center);
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Divider(
thickness = 3.dp,
color = Color.Red)
Divider(
modifier = Modifier.padding(top = 200.dp),
thickness = 3.dp,
color = Color.Black)
}
}
Screen:
Question is: Why a is not equal to b even though both have same padding?
Modifier.padding increases the size of the view. And when it is placed in the center of the Box, it looks like only half of the padding is applied, but the other half of the view is in the top half of the screen.
You can add border to see what's actually going on here:
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Divider(
thickness = 3.dp,
color = Color.Red)
Divider(
modifier = Modifier.border(2.dp, color = Color.Green).padding(top = 200.dp),
thickness = 3.dp,
color = Color.Black)
}
You can use Modifier.offset instead to get the result you expect
Android Jetpack compose Card draws a border around the card when background color has some transparency. This is how it looks in AS:
But this is how it looks in the app:
If I set background to a solid color it works, but by default backgroundColor is a surface color from material (in my app val white850 = Color(0xD9FFFFFF)) and it looks like on the picture above.
#Composable
fun TraitCard(trait: Trait) {
Card(
shape = MaterialTheme.shapes.small,
modifier = Modifier.size(width = 192.dp, height = 56.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Icon(
imageVector = Icons.Rounded.ChildFriendly,
contentDescription = "",
modifier = Modifier
.fillMaxHeight()
.background(color = MaterialTheme.colors.background)
.aspectRatio(1f)
.padding(8.dp),
tint = MaterialTheme.colors.onBackground
)
Text(
text = trait.name,
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
}
}
Does anyone have a clue why it's happening?
This is because of the elevation that Card has by default (and how shadows are drawn), if you remove the elevation this won't happen.
You can try to convert the semitransparent color to the non transparent one with something like:
backgroundColor = Color(0xD9FFFFFF).compositeOver(Color.White),
There's Surface composable in Jetpack Compose which represents a material surface. A surface allows you to setup things like background color or border but it seems that the same might be done using modifiers. When should I use the Surface composable and what the benefits it gives me?
Surface composable makes the code easier as well as explicitly indicates that the code uses a material surface. Let's see an example:
Surface(
color = MaterialTheme.colors.primarySurface,
border = BorderStroke(1.dp, MaterialTheme.colors.secondary),
shape = RoundedCornerShape(8.dp),
elevation = 8.dp
) {
Text(
text = "example",
modifier = Modifier.padding(8.dp)
)
}
and the result:
The same result can be achieved without Surface:
val shape = RoundedCornerShape(8.dp)
val shadowElevationPx = with(LocalDensity.current) { 2.dp.toPx() }
val backgroundColor = MaterialTheme.colors.primarySurface
Text(
text = "example",
color = contentColorFor(backgroundColor),
modifier = Modifier
.graphicsLayer(shape = shape, shadowElevation = shadowElevationPx)
.background(backgroundColor, shape)
.border(1.dp, MaterialTheme.colors.secondary, shape)
.padding(8.dp)
)
but it has a few drawbacks:
The modifiers chain is pretty big and it isn't obvious that it implements a material surface
I have to declare a variable for the shape and pass it into three different modifiers
It uses contentColorFor to figure out the content color while Surface does it under the hood. As a result the backgroundColor is used in two places as well.
I have to calculate the elevation in pixels
Surface adjusts colors for elevation (in case of dark theme) according to the material design. If you want the same behavior, it should be handled manually.
For the full list of Surface features it's better to take a look at the documentation.
Surface is a Box with a Modifier.surface() and material colors and elevation, it checks elevation of ancestors to be always on top of them, and only overload below blocking touch propagation behind the surface with pointerInput(Unit) {}.
#Composable
fun Surface(
modifier: Modifier = Modifier,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp = 0.dp,
content: #Composable () -> Unit
) {
val absoluteElevation = LocalAbsoluteElevation.current + elevation
CompositionLocalProvider(
LocalContentColor provides contentColor,
LocalAbsoluteElevation provides absoluteElevation
) {
Box(
modifier = modifier
.surface(
shape = shape,
backgroundColor = surfaceColorAtElevation(
color = color,
elevationOverlay = LocalElevationOverlay.current,
absoluteElevation = absoluteElevation
),
border = border,
elevation = elevation
)
.semantics(mergeDescendants = false) {}
.pointerInput(Unit) {},
propagateMinConstraints = true
) {
content()
}
}
}
And Modifier.surface()
private fun Modifier.surface(
shape: Shape,
backgroundColor: Color,
border: BorderStroke?,
elevation: Dp
) = this.shadow(elevation, shape, clip = false)
.then(if (border != null) Modifier.border(border, shape) else Modifier)
.background(color = backgroundColor, shape = shape)
.clip(shape)
Another interesting thing is it is Box with propagateMinConstraints = true parameter which forces first descendant to have same minimum constraints or dimensions
Surface(
modifier = Modifier.size(200.dp),
onClick = {}) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {}
}
Spacer(modifier = Modifier.height(20.dp))
Surface(
modifier = Modifier.size(200.dp),
onClick = {}) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {
Box(
modifier = Modifier
.size(50.dp)
.background(Color.Green, RoundedCornerShape(6.dp))
)
}
}
Spacer(modifier = Modifier.height(20.dp))
Box(
modifier = Modifier.size(200.dp)
) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {
Box(
modifier = Modifier
.size(50.dp)
.background(Color.Green, RoundedCornerShape(6.dp))
)
}
}
In first example on Surface forces Column to have 200.dp size even though it has Modifier.size(50.dp).
In second example Box inside Column has 50.dp size because it's not a direct descendant of Surface.
In third example if we replace Surface(Box with propagateMinConstraints true) with Box it allows direct descendant to use its own constraints or dimensions.
Surface is the equivalent of CardView in view system.
By Surface, you can set elevation for the view (note that this is not the same with Modifier.shadow)