I want to add a Floating Action Button with a gradient background in Jetpack Compose. I have the following snippet to do so:
FloatingActionButton(
onClick = {
coroutineScope.safeLaunch {
navController.navigate("AddTodoPage") {
launchSingleTop = true
}
}
},
shape = RoundedCornerShape(14.dp),
backgroundColor = Color.Transparent,
modifier = Modifier
.constrainAs(addFab) {
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
}
.offset(x = (-16).dp, y = (-24).dp)
.background(
brush = Brush.verticalGradient(
colors = BluePinkGradient()
),
shape = RoundedCornerShape(14.dp)
)
) {
Icon(
painter = painterResource(id = R.drawable.ic_add),
contentDescription = "Add icon",
tint = Color.White
)
}
fun BluePinkGradient(inverse: Boolean = false) = when (inverse) {
true -> listOf(
MutedBlue,
MutedPink
)
false -> listOf(
MutedPink,
MutedBlue
)
}
val MutedBlue = Color(0xFF26A69A)
val MutedPink = Color(0xFFEC407A)
But from the image below, the button has a "Whitish" shade on the plus icon. How can I remove that shade or a better way to set the FAB background to a gradient?
Fab Image
'"Whitish" shade on the plus icon' is the result of elevation parameter. You can zero it, but it doesn't looks like you need FAB in the first place.
As you need to custom the button that much, you can use IconButton instead:
IconButton(
onClick = {
},
modifier = Modifier
.background(
brush = Brush.verticalGradient(
colors = BluePinkGradient()
),
shape = RoundedCornerShape(14.dp)
)
) {
Icon(
painter = painterResource(id = R.drawable.ic_undo),
contentDescription = "Add icon",
tint = Color.White
)
}
FloatingActionButton is only applying some Material defaults to the content, it doesn't make it really floating, it has to be done with the container.
I have developed the following solution, which I have confirmed as working:
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun CrazyFloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
gradient: List<Color>,
contentColor: Color = contentColorFor(gradient[0]),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
content: #Composable () -> Unit
) {
Surface(
modifier = modifier,
shape = shape,
contentColor = contentColor,
elevation = elevation.elevation(interactionSource).value,
onClick = onClick,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple()
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(MaterialTheme.typography.button) {
Box(
modifier = Modifier
.defaultMinSize(minWidth = 56.dp, minHeight = 56.dp)
.background(brush = Brush.verticalGradient(gradient)),
contentAlignment = Alignment.Center
) { content() }
}
}
}
}
Just prepend Crazy to your Composable and you should be good to go.
Related
For some reason, I'm unable to change the color of the ripple effect of a Button. What am I doing wrong here?
androidx.compose.material.Button(
onClick = onClick,
modifier = modifier
.indication(
interactionSource = interactionSource,
indication = rememberRipple(color = Color.Red)
)
.navigationBarsPadding(),
enabled = enabled,
interactionSource = interactionSource,
elevation = elevation,
shape = RectangleShape,
colors = ButtonDefaults.bottomColors(),
contentPadding = PaddingValues(vertical = 4.dp),
content = { Text(text) },
)
Button is derived from this Surface which has its own indication with rememberRipple(). Since Button is as Surface, you can implement clickable with any other Composable that resembles Button
#Composable
fun Button(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.elevation(),
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.buttonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: #Composable RowScope.() -> Unit
) {
val contentColor by colors.contentColor(enabled)
Surface(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = shape,
color = colors.backgroundColor(enabled).value,
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
interactionSource = interactionSource,
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(
value = MaterialTheme.typography.button
) {
Row(
Modifier
.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight
)
.padding(contentPadding),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
}
}
#Composable
fun Surface(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp = 0.dp,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: #Composable () -> Unit
) {
val absoluteElevation = LocalAbsoluteElevation.current + elevation
CompositionLocalProvider(
LocalContentColor provides contentColor,
LocalAbsoluteElevation provides absoluteElevation
) {
Box(
modifier = modifier
.minimumTouchTargetSize()
.surface(
shape = shape,
backgroundColor = surfaceColorAtElevation(
color = color,
elevationOverlay = LocalElevationOverlay.current,
absoluteElevation = absoluteElevation
),
border = border,
elevation = elevation
)
.clickable(
interactionSource = interactionSource,
indication = rememberRipple(),
enabled = enabled,
role = Role.Button,
onClick = onClick
),
propagateMinConstraints = true
) {
content()
}
}
}
Your code doesn't work because the ripple is implemented in the clickable modifier defined inside the Button.
However you can change the appearance of the ripple defining a custom RippleTheme and applying it to your composable with the LocalRippleTheme.
Something like:
private object RedRippleTheme: RippleTheme {
#Composable
override fun defaultColor() =
RippleTheme.defaultRippleColor(
Color.Red,
lightTheme = true
)
#Composable
override fun rippleAlpha(): RippleAlpha =
RippleTheme.defaultRippleAlpha(
Color.Black,
lightTheme = true
)
}
and:
CompositionLocalProvider(LocalRippleTheme provides RedRippleTheme) {
Button(
//....
){
//Button content
}
}
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
I want to create button where I have only text and icon and all background and borders are transparent. I create something like that:
OutlinedButton(
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent),
border = BorderStroke(0.dp, Color.Transparent),
modifier = modifier,
onClick = onClick
) {
icon?.invoke()
Text(
text = value,
fontSize = 12.sp
)
}
and everything is ok, but I lost default colors(should be blue, and I have black icon and text). How can I remove all background and borders from button but still have theme colors?
Could you try this?
#Composable
fun TiledButton(
onClick: () -> Unit,
#DrawableRes backgroundDrawableId: Int,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
contentColor: Color = MaterialTheme.colors.primary,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: #Composable RowScope.() -> Unit
) {
Button(
onClick = onClick,
contentPadding = PaddingValues(0.dp),
enabled = enabled,
shape = shape,
border = border,
elevation = null,
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Transparent,
contentColor = contentColor,
disabledBackgroundColor = Color.Transparent,
disabledContentColor = contentColor.copy(alpha = ContentAlpha.disabled),
),
modifier = modifier
) {
Box(
contentAlignment = Alignment.Center,
) {
TileAndroidImage(
drawableId = backgroundDrawableId,
contentDescription = "...",
modifier = Modifier.matchParentSize()
)
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(contentPadding),
content = content,
)
}
}
}
TiledButton(
onClick = { },
backgroundDrawableId = R.drawable.tile,
border = BorderStroke(1.dp, Color.Blue),
) {
Text("Button")
}
Just use a TextButton instead of a OutlinedButton:
TextButton(
onClick = { }
) {
Icon(Icons.Default.Add,"")
Text(
text = value,
fontSize = 12.sp
)
}
If you want to use a OutlinedButton just use ButtonDefaults.outlinedButtonColors instead of ButtonDefaults.buttonColors:
OutlinedButton(
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = Color.Transparent),
border = BorderStroke(0.dp, Color.Transparent),
modifier = modifier,
onClick = onClick
) {
Icon(Icons.Default.Add,"")
Text(
text = value,
fontSize = 12.sp
)
}
I am trying to display a Button with rounded corners and a 50% transparent background. My current attempt looks like this:
MaterialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.Yellow
) {
Column(modifier = Modifier.padding(10.dp)) {
Button(
modifier = Modifier
.clip(CircleShape),
onClick = { },
colors = ButtonDefaults.buttonColors(backgroundColor = Color.White.copy(alpha = 0.5f))
) {
Text(
text = "My Button",
textAlign = TextAlign.Center
)
}
}
}
}
The result is not very pretty:
It looks like the issue is with with the shading, but I'm not sure how to remove it and just show the same color within the whole shape.
Turns out the shadow will disappear when the elevation is removed.
Button(
modifier = Modifier
.clip(CircleShape),
onClick = { },
elevation = null,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.White.copy(alpha = 0.5f))
) { ... }
Button is just a Surface wrapping the content that you provide. You could check the source. So, I just tweaked it a little
#Composable
fun HollowButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.elevation(),
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.buttonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: #Composable RowScope.() -> Unit
) {
val contentColor by colors.contentColor(enabled)
Surface(
modifier = modifier,
shape = shape,
color = colors.backgroundColor(enabled).value.copy(0.5f), //Basically I refactored the alpha modification to here
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
onClick = onClick,
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple()
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(
value = MaterialTheme.typography.button
) {
Row(
Modifier
.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight
)
.padding(contentPadding),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
}
}
Works like a charm.
So these are my Project Versions:
val compose_version by extra("1.0.0-beta07")
val kotlin_version by extra("1.4.32")
val hilt_version by extra("2.36")
So I have a LazyColum which displays a custom composable card list:
package com.veloce.montageservice.ui.composables
// Here are imports but its too much code for stackoverflow
#Composable
fun SimpleCard(
imageVector: ImageVector,
title: String,
description: String,
#DrawableRes trailingButton: Int? = null,
clickable: () -> Unit
) {
Card(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
.clickable {
clickable()
},
border = BorderStroke(
(0.5).dp,
brush = Brush.horizontalGradient(
colors = listOf(
MaterialTheme.colors.primary, MaterialTheme.colors.primaryVariant
)
)
),
elevation = 5.dp
// backgroundColor = Color.Gray
) {
Row(
modifier = Modifier.padding(8.dp),
horizontalArrangement = if (trailingButton != null) Arrangement.SpaceEvenly else Arrangement.Start
) {
Surface(
shape = CircleShape,
modifier = Modifier.size(50.dp),
color = MaterialTheme.colors.primaryVariant
) {
Icon(
modifier = Modifier.padding(4.dp),
imageVector = imageVector,
contentDescription = "Icon of a building",
tint = Color.White
)
}
Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.SpaceEvenly
) {
Text(
title,
fontWeight = FontWeight.Bold
)
Text(description)
}
trailingButton?.let {
Surface(
shape = CircleShape,
modifier = Modifier.size(35.dp),
color = MaterialTheme.colors.primaryVariant
) {
Icon(
modifier = Modifier.padding(4.dp),
imageVector = ImageVector.vectorResource(id = it),
contentDescription = "Icon of a building",
tint = Color.White
)
}
}
}
}
}
And this should be clickable. It was, but now it's not anymore and I didn't have changed anything in this composable.
Has anyone an idea what went wrong? The scroll function of LazyColumnworks perfectly fine, it's just that I can't click it anymore from one day to the other.
In compose 1.0.0-beta08 there was a breaking API Change (https://developer.android.com/jetpack/androidx/releases/compose-material) which causes the clickable modifier to be ignored. You have to use the onClickparameter of Card instead:
Card(onClick = { count++ }) {
Text("Clickable card content with count: $count")
}