From 92df8054cec7fcfa7f083c050835097d5851dabc Mon Sep 17 00:00:00 2001 From: Radmir Date: Fri, 10 Apr 2026 19:30:15 +0500 Subject: [PATCH 1/2] Consolidate text field components and improve GemTextField - Remove duplicate AddressChainField from add_asset, reuse recipient's version - Remove unused PhraseField component - Extract TransferTextFieldActions to shared ui/components/fields - Rework GemTextField with decorationBox for floating label pattern - Constrain IconButton size in TransferTextFieldActions to reduce field height - Wrap MemoField error in Column with consistent spacing --- .../android/ui/components/PhraseField.kt | 129 ------------------ .../add_asset/presents/build.gradle.kts | 1 + .../features/add_asset/views/AddAssetScene.kt | 1 + .../add_asset/views/AddressChainField.kt | 127 ----------------- .../presents/components/AddressChainField.kt | 44 +----- .../presents/components/MemoField.kt | 65 +++++---- .../android/ui/components/GemTextField.kt | 36 ++++- .../fields/TransferTextFieldActions.kt | 11 +- 8 files changed, 75 insertions(+), 339 deletions(-) delete mode 100644 android/app/src/main/kotlin/com/gemwallet/android/ui/components/PhraseField.kt delete mode 100644 android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddressChainField.kt diff --git a/android/app/src/main/kotlin/com/gemwallet/android/ui/components/PhraseField.kt b/android/app/src/main/kotlin/com/gemwallet/android/ui/components/PhraseField.kt deleted file mode 100644 index 2d33e7adb9..0000000000 --- a/android/app/src/main/kotlin/com/gemwallet/android/ui/components/PhraseField.kt +++ /dev/null @@ -1,129 +0,0 @@ -package com.gemwallet.android.ui.components - -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ContentPaste -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextFieldDefaults -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.platform.LocalClipboard -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp -import com.gemwallet.android.ui.R -import com.gemwallet.android.ui.components.buttons.FieldBottomAction -import com.gemwallet.android.ui.components.clipboard.getPlainText -import com.gemwallet.android.ui.theme.Spacer16 - -@Composable -private fun PhraseTextField( - value: String, - onValueChange: (String) -> Unit, - interactionSource: MutableInteractionSource, - modifier: Modifier = Modifier, - minLines: Int = 1, - decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit, -) { - val clipboardManager = LocalClipboard.current.nativeClipboard - val textStyle = LocalTextStyle.current - val mergedTextStyle = textStyle.merge(TextStyle(color = MaterialTheme.colorScheme.onSurface)) - decorationBox { - Column(modifier = modifier) { - BasicTextField( - modifier = Modifier.fillMaxWidth(), - value = value, - onValueChange = onValueChange, - minLines = minLines, - interactionSource = interactionSource, - textStyle = mergedTextStyle, - cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), - ) - Spacer16() - Box( - modifier = Modifier.fillMaxWidth(), - ) { - FieldBottomAction( - modifier = Modifier - .align(Alignment.CenterEnd), - imageVector = Icons.Default.ContentPaste, - text = stringResource(id = R.string.common_paste), - ) { - onValueChange(clipboardManager.getPlainText() ?: "") - } - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun PhraseField( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - label: String = "", - placeholder: String = "", - minLines: Int = 1, -) { - val interactionSource = remember { MutableInteractionSource() } - PhraseTextField( - modifier = if (label.isNotEmpty()) { - modifier - .semantics(mergeDescendants = true) {} - .padding(top = 8.dp) - } else { - modifier - }, - value = value, - onValueChange = onValueChange, - interactionSource = interactionSource, - minLines = minLines, - ) { innerTextField -> - OutlinedTextFieldDefaults.DecorationBox( - value = value, - innerTextField = innerTextField, - enabled = true, - singleLine = false, - visualTransformation = VisualTransformation.None, - interactionSource = interactionSource, - label = { - Text(text = label) - }, - placeholder = { - Text(text = placeholder) - }, - colors = OutlinedTextFieldDefaults.colors(), - container = { - OutlinedTextFieldDefaults.Container( - enabled = true, - isError = false, - interactionSource = interactionSource, - shape = OutlinedTextFieldDefaults.shape, - colors = OutlinedTextFieldDefaults.colors() - ) - } - ) - } -} -// -//@Composable -//@Preview -//fun PreviewPhraseTextField() { -// WalletTheme { -// PhraseField(value = "", onValueChange = {}) -// } -//} diff --git a/android/features/add_asset/presents/build.gradle.kts b/android/features/add_asset/presents/build.gradle.kts index 725269376c..5b829606eb 100644 --- a/android/features/add_asset/presents/build.gradle.kts +++ b/android/features/add_asset/presents/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(project(":ui")) implementation(project(":features:add_asset:viewmodels")) implementation(project(":features:recipient:viewmodels")) + implementation(project(":features:recipient:presents")) implementation(libs.hilt.android) implementation(libs.hilt.navigation.compose) diff --git a/android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddAssetScene.kt b/android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddAssetScene.kt index c232ad0788..7ffdcbc19f 100644 --- a/android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddAssetScene.kt +++ b/android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddAssetScene.kt @@ -27,6 +27,7 @@ import com.gemwallet.android.ui.components.screen.Scene import com.gemwallet.android.ui.models.ListPosition import com.gemwallet.android.ui.theme.defaultPadding import com.gemwallet.android.features.add_asset.viewmodels.models.TokenSearchState +import com.gemwallet.android.features.recipient.presents.components.AddressChainField import com.wallet.core.primitives.Asset @Composable diff --git a/android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddressChainField.kt b/android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddressChainField.kt deleted file mode 100644 index 716905e4e2..0000000000 --- a/android/features/add_asset/presents/src/main/kotlin/com/gemwallet/android/features/add_asset/views/AddressChainField.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.gemwallet.android.features.add_asset.views - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.CheckCircle -import androidx.compose.material.icons.filled.Error -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.platform.LocalClipboard -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.unit.dp -import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.gemwallet.android.ui.components.GemTextField -import com.gemwallet.android.ui.components.clipboard.getPlainText -import com.gemwallet.android.ui.components.fields.TransferTextFieldActions -import com.gemwallet.android.ui.components.progress.CircularProgressIndicator16 -import com.gemwallet.android.ui.theme.paddingSmall -import com.gemwallet.android.ui.theme.space4 -import com.gemwallet.android.features.recipient.viewmodel.AddressChainViewModel -import com.wallet.core.primitives.Chain -import com.wallet.core.primitives.NameRecord - -@Composable -fun ColumnScope.AddressChainField( - chain: Chain?, - value: String, - label: String, - onValueChange: (String, NameRecord?) -> Unit, - error: String = "", - editable: Boolean = true, - searchName: Boolean = true, - onQrScanner: (() -> Unit)? = null, -) { - val viewModel: AddressChainViewModel = hiltViewModel() - val uiState by viewModel.uiState.collectAsStateWithLifecycle() - - val keyboardController = LocalSoftwareKeyboardController.current - val clipboardManager = LocalClipboard.current.nativeClipboard - - LaunchedEffect(key1 = value) { - viewModel.onNameRecord(chain, value) - } - - LaunchedEffect(key1 = uiState.nameRecord?.address) { - onValueChange(uiState.nameRecord?.name ?: value, uiState.nameRecord) - } - - GemTextField( - modifier = Modifier - .fillMaxWidth() - .onFocusChanged { - if (it.hasFocus) keyboardController?.show() else keyboardController?.hide() - }, - value = value, - singleLine = true, - readOnly = !editable, - label = label, - onValueChange = { newValue -> - if (searchName) { - viewModel.onInput(newValue, chain) - } - onValueChange(newValue, uiState.nameRecord) - }, - trailing = { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(paddingSmall) - ) { - if (uiState.isLoading) { - CircularProgressIndicator16() - } - if (uiState.isResolve) { - Icon( - modifier = Modifier.size(24.dp), - imageVector = Icons.Default.CheckCircle, - contentDescription = "Name is resolved", - tint = MaterialTheme.colorScheme.tertiary, - ) - } - if (uiState.isFail) { - Icon( - modifier = Modifier.size(24.dp), - imageVector = Icons.Default.Error, - contentDescription = "Name is fail", - tint = MaterialTheme.colorScheme.error, - ) - } - TransferTextFieldActions( - value = value, - paste = { - onValueChange( - clipboardManager.getPlainText() ?: "", - uiState.nameRecord - ) - }, - qrScanner = onQrScanner, - onClean = { - onValueChange("", null) - viewModel.onInput("", null) - } - ) - } - } - ) - if (error.isNotEmpty()) { - Spacer(modifier = Modifier.size(space4)) - Text( - modifier = Modifier.fillMaxWidth(), - text = error, - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.labelMedium, - ) - } -} \ No newline at end of file diff --git a/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/AddressChainField.kt b/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/AddressChainField.kt index 353c60bf31..938b1b56fb 100644 --- a/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/AddressChainField.kt +++ b/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/AddressChainField.kt @@ -9,12 +9,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CheckCircle -import androidx.compose.material.icons.filled.Clear -import androidx.compose.material.icons.filled.ContentPaste import androidx.compose.material.icons.filled.Error -import androidx.compose.material.icons.filled.QrCodeScanner import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -31,6 +27,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.gemwallet.android.features.recipient.viewmodel.AddressChainViewModel import com.gemwallet.android.ui.components.GemTextField import com.gemwallet.android.ui.components.clipboard.getPlainText +import com.gemwallet.android.ui.components.fields.TransferTextFieldActions import com.gemwallet.android.ui.components.progress.CircularProgressIndicator16 import com.gemwallet.android.ui.theme.paddingHalfSmall import com.gemwallet.android.ui.theme.paddingSmall @@ -129,42 +126,3 @@ fun ColumnScope.AddressChainField( } } } - -@Composable -fun TransferTextFieldActions( - value: String, - paste: (() -> Unit)? = null, - qrScanner: (() -> Unit)? = null, - onClean: () -> Unit -) { - if (value.isNotEmpty()) { - IconButton(onClick = onClean) { - Icon( - imageVector = Icons.Default.Clear, - contentDescription = "clear", - tint = MaterialTheme.colorScheme.onSurface, - ) - } - return - } - Row { - if (paste != null) { - IconButton(onClick = paste) { - Icon( - imageVector = Icons.Default.ContentPaste, - contentDescription = "paste", - tint = MaterialTheme.colorScheme.onSurface, - ) - } - } - if (qrScanner != null) { - IconButton(onClick = qrScanner) { - Icon( - imageVector = Icons.Default.QrCodeScanner, - contentDescription = "scan_address", - tint = MaterialTheme.colorScheme.onSurface, - ) - } - } - } -} diff --git a/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/MemoField.kt b/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/MemoField.kt index c1ec856c2a..7e5b713050 100644 --- a/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/MemoField.kt +++ b/android/features/recipient/presents/src/main/kotlin/com/gemwallet/android/features/recipient/presents/components/MemoField.kt @@ -1,8 +1,8 @@ package com.gemwallet.android.features.recipient.presents.components -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -13,7 +13,8 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import com.gemwallet.android.features.recipient.viewmodel.models.RecipientError import com.gemwallet.android.ui.components.GemTextField import com.gemwallet.android.ui.components.clipboard.getPlainText -import com.gemwallet.android.ui.theme.space4 +import com.gemwallet.android.ui.components.fields.TransferTextFieldActions +import com.gemwallet.android.ui.theme.paddingHalfSmall @Composable fun MemoTextField( @@ -25,34 +26,38 @@ fun MemoTextField( ) { val keyboardController = LocalSoftwareKeyboardController.current val clipboardManager = LocalClipboard.current.nativeClipboard - GemTextField( - modifier = Modifier - .fillMaxWidth() - .onFocusChanged { - if (it.hasFocus) keyboardController?.show() else keyboardController?.hide() - }, - value = value, - singleLine = true, - label = label, - onValueChange = onValueChange, - trailing = { - TransferTextFieldActions( - value = value, - paste = { onValueChange(clipboardManager.getPlainText() ?: "") }, - qrScanner = onQrScanner, - onClean = { - onValueChange("") - } + Column( + modifier = Modifier, + verticalArrangement = Arrangement.spacedBy(paddingHalfSmall), + ) { + GemTextField( + modifier = Modifier + .fillMaxWidth() + .onFocusChanged { + if (it.hasFocus) keyboardController?.show() else keyboardController?.hide() + }, + value = value, + singleLine = true, + label = label, + onValueChange = onValueChange, + trailing = { + TransferTextFieldActions( + value = value, + paste = { onValueChange(clipboardManager.getPlainText() ?: "") }, + qrScanner = onQrScanner, + onClean = { + onValueChange("") + } + ) + } + ) + if (error != RecipientError.None) { + Text( + modifier = Modifier.fillMaxWidth(), + text = recipientErrorString(error), + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.labelMedium, ) } - ) - if (error != RecipientError.None) { - Spacer(modifier = Modifier.size(space4)) - Text( - modifier = Modifier.fillMaxWidth(), - text = recipientErrorString(error), - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.labelMedium, - ) } } \ No newline at end of file diff --git a/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt b/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt index 0a1d42b267..fb98e4e6b9 100644 --- a/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt +++ b/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt @@ -1,9 +1,11 @@ package com.gemwallet.android.ui.components import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions @@ -14,6 +16,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.unit.dp import com.gemwallet.android.ui.components.list_item.listItem import com.gemwallet.android.ui.models.ListPosition import com.gemwallet.android.ui.theme.Spacer4 @@ -40,12 +43,8 @@ fun GemTextField( .padding(paddingDefault), verticalArrangement = Arrangement.Center, ) { - Text( - text = label, - color = MaterialTheme.colorScheme.secondary, - style = MaterialTheme.typography.bodySmall, - ) - Row ( + Row( + modifier = Modifier.heightIn(min = 36.dp), verticalAlignment = Alignment.CenterVertically, ) { BasicTextField( @@ -60,6 +59,29 @@ fun GemTextField( keyboardActions = keyboardActions, keyboardOptions = keyboardOptions, cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + decorationBox = { innerTextField -> + if (value.isNotEmpty() && label.isNotEmpty()) { + Column { + Text( + text = label, + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodySmall, + ) + innerTextField() + } + } else { + Box(contentAlignment = Alignment.CenterStart) { + if (label.isNotEmpty()) { + Text( + text = label, + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodyLarge, + ) + } + innerTextField() + } + } + } ) trailing?.invoke() } @@ -73,4 +95,4 @@ fun GemTextField( ) } } -} \ No newline at end of file +} diff --git a/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/fields/TransferTextFieldActions.kt b/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/fields/TransferTextFieldActions.kt index b0278955fe..c04570db25 100644 --- a/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/fields/TransferTextFieldActions.kt +++ b/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/fields/TransferTextFieldActions.kt @@ -1,6 +1,7 @@ package com.gemwallet.android.ui.components.fields import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.ContentPaste @@ -9,6 +10,10 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +private val IconButtonSize = 36.dp @Composable fun TransferTextFieldActions( @@ -18,7 +23,7 @@ fun TransferTextFieldActions( onClean: () -> Unit ) { if (value.isNotEmpty()) { - IconButton(onClick = onClean) { + IconButton(modifier = Modifier.size(IconButtonSize), onClick = onClean) { Icon( imageVector = Icons.Default.Clear, contentDescription = "clear", @@ -29,7 +34,7 @@ fun TransferTextFieldActions( } Row { if (paste != null) { - IconButton(onClick = paste) { + IconButton(modifier = Modifier.size(IconButtonSize), onClick = paste) { Icon( imageVector = Icons.Default.ContentPaste, contentDescription = "paste", @@ -38,7 +43,7 @@ fun TransferTextFieldActions( } } if (qrScanner != null) { - IconButton(onClick = qrScanner) { + IconButton(modifier = Modifier.size(IconButtonSize), onClick = qrScanner) { Icon( imageVector = Icons.Default.QrCodeScanner, contentDescription = "scan_address", From d8ccc7c9ecf6f08a69915112bf4698682ed39e1a Mon Sep 17 00:00:00 2001 From: Radmir Date: Sat, 11 Apr 2026 19:17:05 +0500 Subject: [PATCH 2/2] Match Android floating label behavior to iOS layout --- .../android/ui/components/GemTextField.kt | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt b/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt index fb98e4e6b9..9d839f3a50 100644 --- a/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt +++ b/android/ui/src/main/kotlin/com/gemwallet/android/ui/components/GemTextField.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions @@ -16,11 +17,16 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp import com.gemwallet.android.ui.components.list_item.listItem import com.gemwallet.android.ui.models.ListPosition import com.gemwallet.android.ui.theme.Spacer4 import com.gemwallet.android.ui.theme.paddingDefault +import com.gemwallet.android.ui.theme.space10 + +private const val FloatingLabelScale = 0.8f @Composable fun GemTextField( @@ -36,6 +42,8 @@ fun GemTextField( keyboardActions: KeyboardActions = KeyboardActions.Default, listPosition: ListPosition = ListPosition.Single, ) { + val hasFloatingLabel = value.isNotEmpty() && label.isNotEmpty() + Column( modifier = modifier .fillMaxWidth() @@ -60,24 +68,27 @@ fun GemTextField( keyboardOptions = keyboardOptions, cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), decorationBox = { innerTextField -> - if (value.isNotEmpty() && label.isNotEmpty()) { - Column { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.CenterStart, + ) { + if (label.isNotEmpty()) { Text( + modifier = Modifier + .graphicsLayer { + scaleX = if (hasFloatingLabel) FloatingLabelScale else 1f + scaleY = if (hasFloatingLabel) FloatingLabelScale else 1f + transformOrigin = TransformOrigin(0f, 0f) + } + .offset(y = if (hasFloatingLabel) -space10 else 0.dp), text = label, color = MaterialTheme.colorScheme.secondary, - style = MaterialTheme.typography.bodySmall, + style = MaterialTheme.typography.bodyLarge, ) - innerTextField() } - } else { - Box(contentAlignment = Alignment.CenterStart) { - if (label.isNotEmpty()) { - Text( - text = label, - color = MaterialTheme.colorScheme.secondary, - style = MaterialTheme.typography.bodyLarge, - ) - } + Box( + modifier = Modifier.offset(y = if (hasFloatingLabel) space10 else 0.dp), + ) { innerTextField() } }