I am working on implementing a customized TextField using BasicTextField in my application, but I have encountered three issues that I haven't been able to resolve.
Colors not applying: Despite setting the colors, they are not reflecting in the UI as expected.
VisualTransformation not modifying text: I’ve tried using a custom visualTransformation to format or mask the text input, but it’s not applying any transformations to the text.
isError not changing the color: The isError flag is not updating the color of the BasicTextField as it should when there is an error, even though I've set it to trigger a color change.
I’ve tried debugging these issues, but I have been unable to figure out why the parameters are not working as intended. Any guidance or suggestions would be greatly appreciate
/**
* A composable function that represents a styled text field, similar to the input fields in
Spotify's UI.
*
* @param value The current value of the text field.
* @param onValueChange A lambda function that handles changes to the text field's value.
* @param modifier Modifier to customize the layout and styling of the text field.
* @param trailingIcon An optional composable that adds an icon to the end of the text field
(e.g., a clear button or search icon).
* @param visualTransformation A transformation applied to the text for visual effects, like
masking (e.g., password input). Defaults to no transformation.
* @param keyboardType Specifies the type of keyboard to display (e.g., text, number, email).
Defaults to `KeyboardType.Text`.
* @param imeAction Specifies the action button on the keyboard (e.g., Done, Next). Defaults
to `ImeAction.Done`.
* @param readOnly Boolean that determines whether the text field is read-only. Defaults to
`false`.
*/
@SuppressLint("UnrememberedMutableInteractionSource")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SpotifyTextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier,
trailingIcon: @Composable() (() -> Unit)? = null,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardType: KeyboardType = KeyboardType.Text,
imeAction: ImeAction = ImeAction.Done,
keyboardActions: KeyboardActions = KeyboardActions.Default,
readOnly: Boolean = false,
isError: Boolean = false
) {
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier
.fillMaxWidth()
.height(48.dp)
// .background(
// color = MaterialTheme.colorScheme.tertiary,
// shape = RoundedCornerShape(4.dp)
// )
.padding(start = 4.dp, end = 4.dp),
singleLine = true,
textStyle = MaterialTheme.typography.bodyLarge,
readOnly = readOnly,
keyboardOptions = KeyboardOptions(
keyboardType = keyboardType, imeAction = imeAction,
hintLocales = LocaleList(Locale("en"))
),
keyboardActions = keyboardActions,
decorationBox = {innerTextField ->
TextFieldDefaults.DecorationBox(
value = value.text,
isError = isError,
innerTextField = { innerTextField() },
visualTransformation = visualTransformation,
trailingIcon = trailingIcon,
shape = RoundedCornerShape(4.dp),
colors = TextFieldDefaults.colors(
unfocusedContainerColor = MaterialTheme.colorScheme.secondary,
cursorColor = MaterialTheme.colorScheme.onTertiary,
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
focusedContainerColor = MaterialTheme.colorScheme.tertiary,
disabledLabelColor = MaterialTheme.colorScheme.tertiary,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
errorTextColor = MaterialTheme.colorScheme.onErrorContainer, //colorResource(id = R.color.brown),
errorContainerColor = MaterialTheme.colorScheme.errorContainer, //colorResource(id = R.color.white_smoke),
errorCursorColor = MaterialTheme.colorScheme.error,
errorTrailingIconColor = MaterialTheme.colorScheme.error,
errorIndicatorColor = Color.Transparent,
),
contentPadding = PaddingValues(8.dp),
container = { },
enabled = true,
singleLine = true,
interactionSource = remember { MutableInteractionSource() }
)
}
)
}
/**
* A composable function for a password input field with a toggleable visibility icon.
* The field allows users to show or hide their password by clicking an icon.
*
* @param value The current value of the password field.
* @param onValueChange A lambda function that handles changes to the password field's value.
*/
@Composable
fun SpotifyPasswordField(
modifier: Modifier = Modifier,
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
imeAction: ImeAction = ImeAction.Done,
isError: Boolean = false,
keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
/** showPassword -> A mutable state that determines whether the password should be shown
(true) or hidden (false).*/
val showPassword = remember { mutableStateOf(false) }
/** Calls the SpotifyTextField composable to create the password field. */
SpotifyTextField(
modifier = modifier,
value = value,
onValueChange = onValueChange,
isError = isError,
trailingIcon = {
/** Displays a toggle button to show or hide the password.*/
if (showPassword.value) {
/** When the password is visible, display the "visibility" icon. */
IconButton(onClick = { showPassword.value = false }) {
Icon(
imageVector = SpotifyIcons.Visibility,
contentDescription = "TAG_SHOW_PASSWORD_ICON"
)
}
} else {
/** When the password is hidden, display the "visibility off" icon. */
IconButton(onClick = { showPassword.value = true }) {
Icon(
imageVector = SpotifyIcons.VisibilityOff,
contentDescription = "TAG_HIDE_PASSWORD_ICON"
)
}
}
},
visualTransformation = if (showPassword.value) {
/** No transformation applied when the password is shown. */
VisualTransformation.None
} else {
/** Applies password masking transformation when the password is hidden. */
PasswordVisualTransformation()
},
imeAction = imeAction,
keyboardType = KeyboardType.Password,
keyboardActions = keyboardActions
)
}
Source: View source