Skip to content

Instantly share code, notes, and snippets.

@MarcinMoskala
Created February 27, 2026 12:30
Show Gist options
  • Select an option

  • Save MarcinMoskala/d3689eee22c61059f44efe16c07355cc to your computer and use it in GitHub Desktop.

Select an option

Save MarcinMoskala/d3689eee22c61059f44efe16c07355cc to your computer and use it in GitHub Desktop.
Incorrect implementation
package academy.kt.ui.component
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.LinearGradientShader
import androidx.compose.ui.graphics.ShaderBrush
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
private val gradientOrangeStart = Color(0xDEFF4E18)
private val gradientOrangeEnd = Color(0x00FF4E18)
private val buttonStrokeColor = Color(0x66FF9500)
@Composable
fun ActionButton(
text: String,
onClick: () -> Unit,
icon: @Composable () -> Unit = {},
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: RoundedCornerShape = RoundedCornerShape(100.dp),
contentPadding: PaddingValues = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
) {
val interactionSource = remember { MutableInteractionSource() }
val isHovered by interactionSource.collectIsHoveredAsState()
// Animates 0→1 on hover; gradient start/end shift right so orange expands
val hoverProgress by animateFloatAsState(
targetValue = if (isHovered) 1f else 0f,
animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing),
label = "buttonHoverProgress",
)
// ShaderBrush so we get the real pixel size for the gradient offset calculation.
// TileMode.Clamp fills the area left of startX with the first stop color (solid orange).
val brush = object : ShaderBrush() {
override fun createShader(size: Size) = LinearGradientShader(
from = Offset(size.width * hoverProgress, 0f),
to = Offset(size.width * (1f + hoverProgress), 0f),
colors = listOf(gradientOrangeStart, gradientOrangeEnd),
tileMode = TileMode.Clamp,
)
}
Button(
onClick = onClick,
enabled = enabled,
shape = shape,
interactionSource = interactionSource,
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = Color.White,
disabledContainerColor = Color.Transparent,
disabledContentColor = Color.White.copy(alpha = 0.38f),
),
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
hoveredElevation = 0.dp,
focusedElevation = 0.dp,
),
border = BorderStroke(1.dp, buttonStrokeColor),
contentPadding = contentPadding,
modifier = modifier
.defaultMinSize(minHeight = 56.dp)
.background(brush, shape),
) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
icon()
Text(
text,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
)
}
}
}
@Preview
@Composable
private fun ActionButtonDefaultPreview() {
Column(
modifier = Modifier
.background(Color(0xFF031217))
.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
ActionButton(text = "Adventure", onClick = {})
ActionButton(text = "Play Again", onClick = {}, icon = { Text("🎮") })
ActionButton(text = "Disabled", onClick = {}, enabled = false)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment