Instantly share code, notes, and snippets.
Created
November 28, 2025 11:06
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save webianks/f2175b097d25835964615feab8577d66 to your computer and use it in GitHub Desktop.
PL MiniApp - HiddenDiscount
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.webianks.miniapps | |
| import android.content.ClipData | |
| import android.content.ClipboardManager | |
| import android.content.Context | |
| import android.graphics.Color as AndroidColor | |
| import android.os.Bundle | |
| import androidx.activity.ComponentActivity | |
| import androidx.activity.SystemBarStyle | |
| import androidx.activity.compose.setContent | |
| import androidx.activity.enableEdgeToEdge | |
| import androidx.compose.animation.AnimatedVisibility | |
| import androidx.compose.animation.core.Animatable | |
| import androidx.compose.animation.core.tween | |
| import androidx.compose.foundation.Canvas | |
| import androidx.compose.foundation.Image | |
| import androidx.compose.foundation.background | |
| import androidx.compose.foundation.border | |
| import androidx.compose.foundation.clickable | |
| import androidx.compose.foundation.gestures.Orientation | |
| import androidx.compose.foundation.gestures.draggable | |
| import androidx.compose.foundation.gestures.rememberDraggableState | |
| import androidx.compose.foundation.interaction.MutableInteractionSource | |
| import androidx.compose.foundation.layout.Arrangement | |
| import androidx.compose.foundation.layout.Box | |
| import androidx.compose.foundation.layout.BoxWithConstraints | |
| import androidx.compose.foundation.layout.Column | |
| import androidx.compose.foundation.layout.Row | |
| import androidx.compose.foundation.layout.Spacer | |
| import androidx.compose.foundation.layout.fillMaxHeight | |
| import androidx.compose.foundation.layout.fillMaxSize | |
| import androidx.compose.foundation.layout.fillMaxWidth | |
| import androidx.compose.foundation.layout.height | |
| import androidx.compose.foundation.layout.offset | |
| import androidx.compose.foundation.layout.padding | |
| import androidx.compose.foundation.layout.size | |
| import androidx.compose.foundation.layout.statusBarsPadding | |
| import androidx.compose.foundation.layout.width | |
| import androidx.compose.foundation.shape.RoundedCornerShape | |
| import androidx.compose.foundation.text.BasicTextField | |
| import androidx.compose.material.icons.Icons | |
| import androidx.compose.material.icons.automirrored.filled.ArrowBack | |
| import androidx.compose.material3.Button | |
| import androidx.compose.material3.ButtonDefaults | |
| import androidx.compose.material3.Icon | |
| import androidx.compose.material3.MaterialTheme | |
| import androidx.compose.material3.Scaffold | |
| import androidx.compose.material3.Snackbar | |
| import androidx.compose.material3.SnackbarHost | |
| import androidx.compose.material3.SnackbarHostState | |
| import androidx.compose.material3.Surface | |
| import androidx.compose.material3.Text | |
| import androidx.compose.material3.Typography | |
| import androidx.compose.runtime.Composable | |
| import androidx.compose.runtime.getValue | |
| import androidx.compose.runtime.mutableStateOf | |
| import androidx.compose.runtime.remember | |
| import androidx.compose.runtime.rememberCoroutineScope | |
| import androidx.compose.runtime.setValue | |
| import androidx.compose.ui.Alignment | |
| import androidx.compose.ui.Modifier | |
| import androidx.compose.ui.draw.clip | |
| import androidx.compose.ui.geometry.Rect | |
| import androidx.compose.ui.geometry.Size | |
| import androidx.compose.ui.graphics.Brush | |
| import androidx.compose.ui.graphics.Color | |
| import androidx.compose.ui.graphics.Outline | |
| import androidx.compose.ui.graphics.Path | |
| import androidx.compose.ui.graphics.PathEffect | |
| import androidx.compose.ui.graphics.PathOperation | |
| import androidx.compose.ui.graphics.RectangleShape | |
| import androidx.compose.ui.graphics.Shape | |
| import androidx.compose.ui.platform.LocalContext | |
| import androidx.compose.ui.platform.LocalDensity | |
| import androidx.compose.ui.res.painterResource | |
| import androidx.compose.ui.text.TextStyle | |
| import androidx.compose.ui.text.font.Font | |
| import androidx.compose.ui.text.font.FontFamily | |
| import androidx.compose.ui.text.font.FontWeight | |
| import androidx.compose.ui.text.style.TextAlign | |
| import androidx.compose.ui.unit.Density | |
| import androidx.compose.ui.unit.Dp | |
| import androidx.compose.ui.unit.IntOffset | |
| import androidx.compose.ui.unit.LayoutDirection | |
| import androidx.compose.ui.unit.dp | |
| import androidx.compose.ui.unit.sp | |
| import kotlinx.coroutines.CoroutineScope | |
| import kotlinx.coroutines.launch | |
| import kotlin.math.roundToInt | |
| // --- Colors & Styles --- | |
| val C_Background = Color(0xFFF6F2ED) | |
| val C_Surface = Color(0xFFFFFFFF) | |
| val C_Outline = Color(0xFFDFDDDB) | |
| val C_Outline_Alt = Color(0xFFFFFFFF).copy(alpha = 0.2f) | |
| val C_Text_Primary = Color(0xFF211304) | |
| val C_Text_Disabled = Color(0xFF9A9795) | |
| val C_Text_Alt = Color(0xFFFFFFFF) | |
| val C_Text_On_Discount = Color(0xFFFFFFFF).copy(alpha = 0.7f) | |
| val C_Discount_End = Color(0xFF7C1414) | |
| val C_Discount_Start = Color(0xFF9E2A2A) | |
| val C_Snackbar = Color(0xFF211304).copy(alpha = 0.8f) | |
| class MainActivity : ComponentActivity() { | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| enableEdgeToEdge( | |
| statusBarStyle = SystemBarStyle.light( | |
| AndroidColor.TRANSPARENT, AndroidColor.TRANSPARENT | |
| ), | |
| navigationBarStyle = SystemBarStyle.light( | |
| AndroidColor.TRANSPARENT, AndroidColor.TRANSPARENT | |
| ) | |
| ) | |
| super.onCreate(savedInstanceState) | |
| setContent { | |
| MaterialTheme( | |
| typography = AppTypography | |
| ) { | |
| HiddenDiscountScreen() | |
| } | |
| } | |
| } | |
| } | |
| @Composable | |
| fun HiddenDiscountScreen() { | |
| val snackbarHostState = remember { SnackbarHostState() } | |
| val scope = rememberCoroutineScope() | |
| // State | |
| var promoCodeInput by remember { mutableStateOf("") } | |
| var isDiscountApplied by remember { mutableStateOf(false) } | |
| var showInvalidCodeError by remember { mutableStateOf(false) } | |
| // Product Details | |
| val originalPrice = 199 | |
| val discountedPrice = 149 | |
| val currentPrice = if (isDiscountApplied) discountedPrice else originalPrice | |
| val offsetX = remember { Animatable(0f) } | |
| fun copyToClipboard(context: Context, text: String) { | |
| val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager | |
| val clip = ClipData.newPlainText("Promo Code", text) | |
| clipboard.setPrimaryClip(clip) | |
| scope.launch { | |
| snackbarHostState.showSnackbar("Copied!") | |
| } | |
| } | |
| fun applyPromoCode() { | |
| if (promoCodeInput.trim().equals("BF2025", ignoreCase = true)) { | |
| isDiscountApplied = true | |
| showInvalidCodeError = false | |
| scope.launch { | |
| offsetX.animateTo(0f, animationSpec = tween(300)) | |
| snackbarHostState.showSnackbar("Discount applied") | |
| } | |
| } else { | |
| showInvalidCodeError = true | |
| } | |
| } | |
| Scaffold( | |
| containerColor = C_Background, | |
| snackbarHost = { | |
| SnackbarHost(hostState = snackbarHostState) { data -> | |
| Snackbar( | |
| modifier = Modifier | |
| .height(32.dp) | |
| .padding(horizontal = 16.dp), | |
| containerColor = C_Snackbar, | |
| contentColor = C_Text_Alt, | |
| shape = RectangleShape | |
| ) { | |
| Text( | |
| text = data.visuals.message, | |
| style = MaterialTheme.typography.bodyMedium, | |
| color = C_Text_Alt, | |
| modifier = Modifier.fillMaxWidth(), | |
| textAlign = TextAlign.Center | |
| ) | |
| } | |
| } | |
| }, | |
| topBar = { | |
| Row( | |
| modifier = Modifier | |
| .fillMaxWidth() | |
| .statusBarsPadding() | |
| .padding(16.dp), | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { | |
| Icon( | |
| imageVector = Icons.AutoMirrored.Filled.ArrowBack, | |
| contentDescription = "Back", | |
| tint = C_Text_Primary | |
| ) | |
| Spacer(modifier = Modifier.weight(1f)) | |
| Text( | |
| text = "Cart", | |
| style = MaterialTheme.typography.titleLarge, | |
| color = C_Text_Primary | |
| ) | |
| Spacer(modifier = Modifier.weight(1f)) | |
| // Spacer to balance the title center | |
| Spacer(modifier = Modifier.size(24.dp)) | |
| } | |
| } | |
| ) { innerPadding -> | |
| Column( | |
| modifier = Modifier | |
| .padding(innerPadding) | |
| .fillMaxSize() | |
| .padding(horizontal = 16.dp), | |
| verticalArrangement = Arrangement.SpaceBetween | |
| ) { | |
| // --- Draggable Product Card Area --- | |
| BoxWithConstraints( | |
| modifier = Modifier | |
| .fillMaxWidth() | |
| .padding(top = 16.dp) | |
| .height(120.dp) | |
| ) { | |
| val revealWidthPx = with(LocalDensity.current) { maxWidth.toPx() } | |
| val velocityThreshold = with(LocalDensity.current) { 100.dp.toPx() } | |
| val draggableState = rememberDraggableState { delta -> | |
| scope.launch { | |
| val newOffset = (offsetX.value + delta).coerceIn(-revealWidthPx, 0f) | |
| offsetX.snapTo(newOffset) | |
| } | |
| } | |
| val onDragStopped: suspend CoroutineScope.(Float) -> Unit = { velocity -> | |
| val targetOffset = | |
| if (offsetX.value < -revealWidthPx / 2 || velocity < -velocityThreshold) { | |
| -revealWidthPx | |
| } else { | |
| 0f | |
| } | |
| offsetX.animateTo(targetOffset, animationSpec = tween(300)) | |
| } | |
| // 1. Background Layer (Promo Panel) | |
| val ticketShape = remember { TicketShape(circleRadius = 8.dp) } | |
| Box( | |
| modifier = Modifier | |
| .align(Alignment.CenterEnd) // Pin to right | |
| .fillMaxHeight() | |
| .fillMaxWidth() | |
| .background( | |
| brush = Brush.horizontalGradient( | |
| colors = listOf(C_Discount_Start, C_Discount_End) | |
| ), | |
| shape = ticketShape | |
| ) | |
| .clip(ticketShape) | |
| ) { | |
| Row( | |
| modifier = Modifier.fillMaxSize(), | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { | |
| Column( | |
| modifier = Modifier | |
| .weight(1f) | |
| .padding(start = 16.dp), | |
| verticalArrangement = Arrangement.Center | |
| ) { | |
| Text( | |
| text = "BLACK FRIDAY SALE", | |
| color = C_Text_On_Discount, | |
| style = MaterialTheme.typography.labelSmall | |
| ) | |
| Row(verticalAlignment = Alignment.CenterVertically) { | |
| Text( | |
| text = "25%", | |
| color = C_Text_Alt, | |
| style = MaterialTheme.typography.displayMedium, | |
| lineHeight = 32.sp | |
| ) | |
| Spacer(modifier = Modifier.width(4.dp)) | |
| Text( | |
| text = "OFF", | |
| color = C_Text_Alt, | |
| style = MaterialTheme.typography.bodyLarge, | |
| ) | |
| } | |
| } | |
| VerticalDashedLine( | |
| modifier = Modifier | |
| .fillMaxHeight(0.6f) | |
| .width(1.dp), | |
| color = C_Outline_Alt, | |
| ) | |
| Column( | |
| modifier = Modifier | |
| .weight(1f) | |
| .padding(end = 16.dp), | |
| horizontalAlignment = Alignment.CenterHorizontally, | |
| verticalArrangement = Arrangement.Center | |
| ) { | |
| Row( | |
| modifier = Modifier | |
| .border(1.dp, C_Outline_Alt, RectangleShape) | |
| .padding(start = 16.dp), | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { | |
| Text( | |
| text = "BF2025", | |
| color = C_Text_Alt, | |
| style = MaterialTheme.typography.bodyMedium | |
| ) | |
| Spacer(modifier = Modifier.width(8.dp)) | |
| val context = LocalContext.current | |
| Box( | |
| modifier = Modifier | |
| .size(40.dp) | |
| .background(C_Surface, RectangleShape) | |
| .clickableNoRipple { copyToClipboard(context, "BF2025") }, | |
| contentAlignment = Alignment.Center | |
| ) { | |
| Icon( | |
| painter = painterResource(id = R.drawable.ic_content_copy), | |
| contentDescription = "Copy", | |
| tint = C_Discount_End, | |
| modifier = Modifier.size(16.dp) | |
| ) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // 2. Foreground Layer (Product Card) | |
| Surface( | |
| shape = RectangleShape, | |
| modifier = Modifier | |
| .offset { IntOffset(offsetX.value.roundToInt(), 0) } | |
| .fillMaxSize() | |
| .draggable( | |
| orientation = Orientation.Horizontal, | |
| state = draggableState, | |
| onDragStopped = onDragStopped | |
| ), | |
| color = C_Surface, | |
| ) { | |
| Row( | |
| modifier = Modifier.padding(12.dp), | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { | |
| Image( | |
| painterResource(R.drawable.img_earphones), | |
| contentDescription = null, | |
| modifier = Modifier.size(96.dp), | |
| ) | |
| Spacer(modifier = Modifier.width(8.dp)) | |
| // Info Column | |
| Column( | |
| modifier = Modifier.fillMaxHeight(), | |
| verticalArrangement = Arrangement.SpaceBetween | |
| ) { | |
| Column { | |
| Text( | |
| text = "Google Pixel Buds Pro", | |
| style = MaterialTheme.typography.bodyLarge, | |
| color = C_Text_Primary | |
| ) | |
| Text( | |
| text = "Noise-cancelling wireless earbuds with rich sound and long battery life.", | |
| style = MaterialTheme.typography.bodySmall, | |
| color = C_Text_Disabled, | |
| maxLines = 2 | |
| ) | |
| } | |
| Row( | |
| modifier = Modifier.fillMaxWidth(), | |
| horizontalArrangement = Arrangement.SpaceBetween, | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { | |
| // Quantity | |
| Row( | |
| verticalAlignment = Alignment.CenterVertically, | |
| horizontalArrangement = Arrangement.spacedBy(20.dp) | |
| ) { | |
| Box( | |
| modifier = Modifier | |
| .size(22.dp) | |
| .border(1.dp, C_Outline, RoundedCornerShape(4.dp)), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| Text( | |
| "—", | |
| style = MaterialTheme.typography.bodyLarge, | |
| color = C_Text_Disabled | |
| ) | |
| } | |
| Text( | |
| "1", | |
| style = MaterialTheme.typography.bodyLarge, | |
| fontWeight = FontWeight.SemiBold, | |
| color = C_Text_Primary | |
| ) | |
| Box( | |
| modifier = Modifier | |
| .size(22.dp) | |
| .border(1.dp, C_Outline, RoundedCornerShape(4.dp)), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| Text( | |
| "+", | |
| style = MaterialTheme.typography.bodyLarge, | |
| color = C_Text_Primary | |
| ) | |
| } | |
| } | |
| // Price | |
| Text( | |
| text = "$$currentPrice", | |
| style = MaterialTheme.typography.headlineSmall, | |
| color = C_Text_Primary | |
| ) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // --- Bottom Section --- | |
| Column( | |
| modifier = Modifier.padding(bottom = 32.dp) | |
| ) { | |
| // Promo Code Input | |
| Row( | |
| modifier = Modifier.fillMaxWidth(), | |
| verticalAlignment = Alignment.Top | |
| ) { | |
| Column(modifier = Modifier.weight(1f)) { | |
| BasicTextField( | |
| value = promoCodeInput, | |
| onValueChange = { | |
| promoCodeInput = it | |
| if (showInvalidCodeError) showInvalidCodeError = false | |
| if (isDiscountApplied) isDiscountApplied = false | |
| }, | |
| singleLine = true, | |
| textStyle = TextStyle( | |
| fontFamily = MaterialTheme.typography.bodyLarge.fontFamily, | |
| fontWeight = FontWeight.Normal, | |
| fontSize = 16.sp, | |
| color = C_Text_Primary | |
| ), | |
| decorationBox = { innerTextField -> | |
| Box( | |
| modifier = Modifier | |
| .fillMaxWidth() | |
| .border( | |
| width = 1.dp, | |
| color = if (showInvalidCodeError) C_Discount_End else C_Outline | |
| ) | |
| .padding(horizontal = 16.dp) | |
| .height(56.dp), | |
| contentAlignment = Alignment.CenterStart | |
| ) { | |
| if (promoCodeInput.isEmpty()) { | |
| Text( | |
| "Enter promo code", | |
| style = MaterialTheme.typography.bodyLarge, | |
| color = C_Text_Disabled | |
| ) | |
| } | |
| innerTextField() | |
| } | |
| } | |
| ) | |
| // Error Message | |
| AnimatedVisibility(visible = showInvalidCodeError) { | |
| Text( | |
| text = "Invalid code", | |
| color = C_Discount_End, | |
| style = MaterialTheme.typography.bodySmall, | |
| modifier = Modifier.padding(top = 4.dp, start = 4.dp) | |
| ) | |
| } | |
| } | |
| // Apply Button | |
| Button( | |
| onClick = { applyPromoCode() }, | |
| enabled = !isDiscountApplied, | |
| colors = ButtonDefaults.buttonColors( | |
| containerColor = C_Outline, | |
| contentColor = C_Text_Primary, | |
| disabledContainerColor = C_Outline.copy(alpha = 0.5f), | |
| disabledContentColor = C_Text_Disabled | |
| ), | |
| shape = RectangleShape, | |
| modifier = Modifier.height(56.dp) | |
| ) { | |
| Text( | |
| text = if (isDiscountApplied) "Applied" else "Apply", | |
| style = MaterialTheme.typography.bodyMedium | |
| ) | |
| } | |
| } | |
| Spacer(modifier = Modifier.height(24.dp)) | |
| Button( | |
| onClick = { }, | |
| modifier = Modifier | |
| .fillMaxWidth() | |
| .height(48.dp), | |
| colors = ButtonDefaults.buttonColors( | |
| containerColor = C_Text_Primary | |
| ), | |
| shape = RectangleShape | |
| ) { | |
| Text( | |
| "Buy", | |
| style = MaterialTheme.typography.bodyLarge, | |
| ) | |
| } | |
| Spacer(modifier = Modifier.height(32.dp)) | |
| } | |
| } | |
| } | |
| } | |
| // Helper to remove ripple for cleaner custom buttons if desired | |
| @Composable | |
| fun Modifier.clickableNoRipple(onClick: () -> Unit): Modifier { | |
| return this.then( | |
| Modifier.clickable( | |
| interactionSource = remember { MutableInteractionSource() }, | |
| indication = null, | |
| onClick = onClick | |
| ) | |
| ) | |
| } | |
| val HostGrotesk = FontFamily( | |
| Font(R.font.host_grotesk_regular, FontWeight.Normal), | |
| Font(R.font.host_grotesk_medium, FontWeight.Medium), | |
| Font(R.font.host_grotesk_semibold, FontWeight.SemiBold), | |
| Font(R.font.host_grotesk_bold, FontWeight.Bold) | |
| ) | |
| val AppTypography = Typography( | |
| // Display Medium | |
| displayMedium = TextStyle( | |
| fontFamily = HostGrotesk, | |
| fontWeight = FontWeight.Bold, | |
| fontSize = 45.sp, | |
| lineHeight = 52.sp, | |
| letterSpacing = 0.sp | |
| ), | |
| // Headline Small | |
| headlineSmall = TextStyle( | |
| fontFamily = HostGrotesk, | |
| fontWeight = FontWeight.Bold, | |
| fontSize = 24.sp, | |
| lineHeight = 28.sp, | |
| letterSpacing = 0.sp | |
| ), | |
| // Title Large | |
| titleLarge = TextStyle( | |
| fontFamily = HostGrotesk, | |
| fontWeight = FontWeight.SemiBold, | |
| fontSize = 22.sp, | |
| lineHeight = 28.sp, | |
| letterSpacing = 0.sp | |
| ), | |
| // Body Large | |
| bodyLarge = TextStyle( | |
| fontFamily = HostGrotesk, | |
| fontWeight = FontWeight.Medium, | |
| fontSize = 16.sp, | |
| lineHeight = 20.sp, | |
| letterSpacing = 0.5.sp | |
| ), | |
| // Body Medium | |
| bodyMedium = TextStyle( | |
| fontFamily = HostGrotesk, | |
| fontWeight = FontWeight.Medium, | |
| fontSize = 14.sp, | |
| lineHeight = 20.sp, | |
| letterSpacing = 0.25.sp | |
| ), | |
| // Body Small | |
| bodySmall = TextStyle( | |
| fontFamily = HostGrotesk, | |
| fontWeight = FontWeight.Normal, | |
| fontSize = 12.sp, | |
| lineHeight = 14.sp, | |
| letterSpacing = 0.4.sp | |
| ), | |
| // Label Small | |
| labelSmall = TextStyle( | |
| fontFamily = HostGrotesk, | |
| fontWeight = FontWeight.Medium, | |
| fontSize = 11.sp, | |
| lineHeight = 16.sp, | |
| letterSpacing = 0.5.sp | |
| ) | |
| ) | |
| class TicketShape(private val circleRadius: Dp) : Shape { | |
| override fun createOutline( | |
| size: Size, | |
| layoutDirection: LayoutDirection, | |
| density: Density | |
| ): Outline { | |
| return Outline.Generic(path = getPath(size, density)) | |
| } | |
| private fun getPath(size: Size, density: Density): Path { | |
| val circleRadiusPx = with(density) { circleRadius.toPx() } | |
| val halfCircle = circleRadiusPx | |
| val middleX = size.width / 2 | |
| val path = Path().apply { | |
| reset() | |
| // Draw a rectangle | |
| addRect(Rect(0f, 0f, size.width, size.height)) | |
| } | |
| val cutoutPath = Path().apply { | |
| // top cutout | |
| addOval( | |
| Rect( | |
| left = middleX - halfCircle, | |
| top = -halfCircle, | |
| right = middleX + halfCircle, | |
| bottom = halfCircle | |
| ) | |
| ) | |
| // bottom cutout | |
| addOval( | |
| Rect( | |
| left = middleX - halfCircle, | |
| top = size.height - halfCircle, | |
| right = middleX + halfCircle, | |
| bottom = size.height + halfCircle | |
| ) | |
| ) | |
| } | |
| return Path.combine( | |
| operation = PathOperation.Difference, | |
| path1 = path, | |
| path2 = cutoutPath | |
| ) | |
| } | |
| } | |
| @Composable | |
| fun VerticalDashedLine( | |
| modifier: Modifier = Modifier, | |
| color: Color = Color.White, | |
| strokeWidth: Dp = 1.dp, | |
| dashWidth: Dp = 4.dp, | |
| gapWidth: Dp = 4.dp | |
| ) { | |
| Canvas(modifier = modifier) { | |
| val pathEffect = PathEffect.dashPathEffect( | |
| intervals = floatArrayOf(dashWidth.toPx(), gapWidth.toPx()), | |
| phase = 0f | |
| ) | |
| drawLine( | |
| color = color, | |
| start = size.copy(width = 0f, height = 0f).let { androidx.compose.ui.geometry.Offset(it.width / 2, 0f) }, | |
| end = size.copy(width = 0f, height = size.height).let { androidx.compose.ui.geometry.Offset(it.width / 2, it.height) }, | |
| strokeWidth = strokeWidth.toPx(), | |
| pathEffect = pathEffect | |
| ) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment