Transparent checkmark in checkBox in Jetpack Compose - android

In my Compose app I need to create a circle checkboxes. I've already achieved that with the code below:
#Composable
fun CircleCheckBox(
isChecked: Boolean,
modifier: Modifier = Modifier,
onChecked: () -> Unit = {},
checkedBackgroundColor: Color,
unCheckedBackgroundColor: Color,
checkedIconColor: Color,
unCheckedIconColor: Color
) {
Box(
modifier = modifier
.clip(CircleShape)
.clickable { onChecked() }
.border(
width = if (!isChecked) 1.dp else 0.dp,
color = if (!isChecked) checkedBackgroundColor else Color.Transparent,
shape = CircleShape
)
.background(
color = if (isChecked) checkedBackgroundColor else unCheckedBackgroundColor,
shape = CircleShape
),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = stringResource(R.string.icon_check),
modifier = Modifier.padding(3.dp),
tint = if (isChecked) checkedIconColor else unCheckedIconColor
)
}
}
But in my app I have a gradient backgrounds on cards, so I want to make checkmarks transparent, but in this realization it's impossible because of the background of the Box. Is there any way to achieve it, like on image below?

You can find an appropriate default icons instead of drawing on your own. Icons.Default.CheckCircle is what you're looking for - it has transparent checkmark inside a filled circle. And you can use Icons.Outlined.Circle instead of border modifier:
#Composable
fun CircleCheckBox(
isChecked: Boolean,
modifier: Modifier = Modifier,
onChecked: () -> Unit = {},
color: Color,
) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.clip(CircleShape)
.clickable { onChecked() }
) {
Icon(
imageVector = if (isChecked) Icons.Default.CheckCircle else Icons.Outlined.Circle,
contentDescription = stringResource(R.string.icon_check),
tint = color
)
}
}
Result:

I think you can achieve this by importing custom icon vector like this
Here, the check is transparent so, it will show the gradient background.

Related

Border radius is not changing based on shape when user click on it jetpack compose

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
)
}
}

Jetpack compose button with elevation and disable state weird behaviour

I created custom button and when I apply elevation with disable state it has because of shadow some small box in middle and it looks like this. Here is also my code:
#Composable
fun PrimaryButton(
modifier: Modifier = Modifier,
enabled: Boolean = false,
text: String,
onClick: () -> Unit,
) {
Button(
onClick = onClick,
Modifier
.height(44.dp)
.shadow(
elevation = 4.dp,
shape = RoundedCornerShape(22.dp),
)
.then(modifier),
colors = ButtonDefaults.buttonColors(
backgroundColor = Style.colors.buttonPrimary,
disabledBackgroundColor = Style.colors.buttonPrimary.copy(0.4f),
),
shape = RoundedCornerShape(22.dp),
enabled = enabled,
) {
Text(
text = text,
style = Style.typography.phoenixTitle,
color = Color.White
)
}
}
fun ButtonSelectedCount(
modifier: Modifier = Modifier,
text: String,
enable: Boolean,
onClick: () -> Unit
) {
Surface(
shape = RoundedCornerShape(8.dp),
modifier = modifier,
elevation = if (enable) 9.dp else 0.dp
) {
Text(
modifier = Modifier
.fillMaxSize()
.background(color = if (enable) Color.Red else Color.Red.copy(alpha = 0.1f))
.clickable(enable) {
onClick()
}
.wrapContentHeight(),
text = text,
style = Typography.button.copy(color = Color.White),
textAlign = TextAlign.Center
)
}
}
I Think, This is what you are looking for. Wish you early success

How to fillMaxSize() the Box() inside row - Compose

I want to align the button in the BottomEnd of the Box in Row (In the BottonEnd of the bellow Card). I have Card with Row who is devided in two parts - Card and Box, and I want the Box to fill max of the rest of the row. I cannot implement it how I would wanted. Bellow I attached the visualization of the current code.
.
#Composable
fun ProductItem(product: ProductModel, onItemClick: () -> Unit, onAddToCardButton: () -> Unit) {
Card(
modifier = Modifier
.fillMaxSize()
.padding(7.dp)
.clickable { onItemClick() },
shape = MaterialTheme.shapes.large,
elevation = 4.dp
) {
Row(
modifier = Modifier.fillMaxSize()
) {
Card(
modifier = Modifier
.weight(1f),
shape = MaterialTheme.shapes.small,
elevation = 2.dp
) {
Image(
painter = painterResource(id = R.drawable.ic_splash_screen),
contentDescription = "Image of ${product.name}",
)
}
Box(
modifier = Modifier
.weight(2f)
.fillMaxHeight()
.padding(6.dp)
.background(Color.Green)
) {
Column(modifier = Modifier.align(Alignment.TopStart)) {
Text(
text = "${product.number}. ${product.name}",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.h4,
)
Text(
text = product.ingredients, fontStyle = FontStyle.Italic
)
}
Button(
modifier = Modifier.align(Alignment.BottomEnd),
onClick = {
onAddToCardButton()
},
shape = RoundedCornerShape(8.dp),
) {
if (product.type == "pizza") {
Text(text = "od ${String.format("%.2f", product.price[0])} zł")
} else {
Text(text = "${String.format("%.2f", product.price[0])} zł")
}
}
}
}
}
}
I expect you display this item in LazyColumn or inside a vertical scrollable.
Modifier.fillMaxHeight doesn't work in this case, because parent height constraint is equal to infinity.
To solve this you ofc can use a static value, but in this case intrinsic measurements can be used to wrap content size.
Add Modifier.height(IntrinsicSize.Max) to your Row.
The code looks good to me at first sight, even tried it and the button is on the bottom right corner. Where do you define the height of your ProductItem card?
Maybe you can try to define it in the code you provided by changing the
.fillMaxSize()
modifier to
.fillMaxWidth()
.requiredHeight(**.dp)
at your first Card composable.
So it would look something like this:
#Composable
fun ProductItem(product: ProductModel, onItemClick: () -> Unit, onAddToCardButton: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.requiredHeight(**.dp)
.padding(7.dp)
.clickable { onItemClick() },
shape = MaterialTheme.shapes.large,
elevation = 4.dp
) {
...
}
As Phil Dukhov answered Modifier.height(IntrinsicSize.Max) works for me.
Just set the modifier to the Root element in your Row. In your case it's Card (change .fillMaxWidth() to .height(IntrinsicSize.Max)):
Card(
modifier = Modifier
.height(IntrinsicSize.Max)
.padding(7.dp)
.clickable { onItemClick() },
shape = MaterialTheme.shapes.large,
elevation = 4.dp
)

Android compose Card has a border when semi-transparent colors used

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),

Ripple with rounded corners Jetpack Compose

In this answer I got wrong ripple animation. Do you know how to create ripple with rounded corners using Jetpack Compose?
With default ripple I have this:
Code:
Card(shape = RoundedCornerShape(30.dp),
border = BorderStroke(width = 2.dp, color = buttonColor(LocalContext.current)),
backgroundColor = backColor(LocalContext.current),
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(radius = 30.dp)
) { show = !show }
) { ... } //Show is animation of other element.
//If I put radius of ripple 200 dp(it's a height of card) ripple works not normal.
Starting with M2 1.0.0-beta08 you can solve this issue using the onClick lambda parameter in the Card instead of the clickable modifier:
Card(
shape = RoundedCornerShape(30.dp),
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
onClick = { show = !show }
){
//card content
}
If you need the clickable or the combinedClickable modifier you have to use the variant without the onClick parameter and to apply also the clip modifier to the Card using the same Card shape:
val shape = RoundedCornerShape(30.dp)
Card(
shape = shape,
modifier = Modifier
//...height, width, padding
.clip(shape)
.combinedClickable(
onLongClick = { /** do something */ },
onClick = { /** do something */ }
)
){
//card content
}
With M3 Card you can do the same.
Until 1.0.0-beta07 applying a .clickable modifier to the Card the ripples aren't clipped by the bounds of the layout.
As workaround you can apply the .clickable modifier to the content of the Card (for example a Box):
Card(
shape = RoundedCornerShape(30.dp),
border = BorderStroke(width = 2.dp, color = Color.Blue),
backgroundColor = Color.White,
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
Box(Modifier
.clickable(
onClick = { /* ...*/ }
)
){
Text("Text")
}
}
I've so far identified 2 options:
In addition to setting the shape, use .clip modifier to clip the Card using the same shape:
Card(
shape = RoundedCornerShape(30.dp),
modifier = Modifier
.clip(RoundedCornerShape(30.dp))
.clickable {
//do something
}
) {
Box {
Text("Text")
}
}
The downside of this approach is that the elevation shadow gets clips as well, so your Card loses it's shadow.
Set the .clickable on the Card content composable:
Card(
shape = RoundedCornerShape(30.dp)
) {
Box(
modifier = Modifier.clickable {
//do something
}
) {
Text("Text")
}
}
Hope this will grant you the easiest solution
Just add .clip(RoundedCornerShape(30.dp)) in the modifier parameter
Here is the full code :
Card(modifier = Modifier
.padding(30.dp)
.size(100.dp)
.clip(RoundedCornerShape(30.dp))
.clickable {
// After click //
}) { }
I checked the sources of Card/Surface composables and found out that you need to have background and clip modifiers with the same shape. So for example the following Box has rounded corner shape and click ripple is cut with the same bounds:
val shape = RoundedCornerShape(16.dp)
Box(
modifier = Modifier
.background(
color = Color.Yellow,
shape = shape
)
.clip(shape)
.clickable { onClick() },
) {
// your content here
}
using rememberRipple(bounded = false) will give a circular ripple effect around the clicked component. It can be used as ->
Modifier.clickable(
indication = rememberRipple(bounded = false),
interactionSource = remember {
MutableInteractionSource()
}
) { }
When you use long press or other gesture,you can use modifier.indication
val interactionSource = remember { MutableInteractionSource() }
Card(
modifier = Modifier
.padding(12.dp, 6.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.indication(interactionSource, LocalIndication.current)
.pointerInput(Unit) {
detectTapGestures(
onPress = { offset ->
val press = PressInteraction.Press(offset)
interactionSource.emit(press)
tryAwaitRelease()
interactionSource.emit(PressInteraction.Release(press))
},
onLongPress = {},
onTap = {}
)
}
)

Categories

Resources