I need to show a success state on Material's TextInputLayout
. I just need the same UI as this layout shows when an error is set. Following is what I need:
class CustomTextInputLayout(
context: Context,
attributes: AttributeSet?,
): TextInputLayout(context, attributes) {
fun setSuccessMode(successText: CharSequence?)
fun setErrorMode(errorText: CharSequence?)
fun setHelperTextMode(helperText: CharSequence?)
}
I am not sure how to do this using themes and styles. I planned to trick TextInputLayout
to show different colours on the basis of a variable, but still use setError()
functionality. But, I don't think I can change theme for TextInputLayout
in runtime easily. Also, introducing a variable will require me to preserve this over savedState
changes.
This is what I ended up using:
/**
* Utility class for managing different states (success, error, helper) of a [TextInputLayout].
*
*
* This utility class allows you to easily switch between success, error, and idle states of a
* TextInputLayout by providing appropriate colors and text for each state.
*
* @param textInputLayout The TextInputLayout to be managed by this utility class.
* @param successColorAttr The theme attribute for the color in success mode.
* Default value is [com.google.android.material.R.attr.colorTertiary].
* @param errorColorAttr The theme attribute for the color in error mode.
* Default value is [com.google.android.material.R.attr.colorError].
* @param idleColorAttr The theme attribute for the color in idle mode.
* Default value is [com.google.android.material.R.attr.colorPrimary].
*/
class TextInputLayoutUtil(
private val textInputLayout: TextInputLayout,
@AttrRes private val successColorAttr: Int = com.google.android.material.R.attr.colorTertiary,
@AttrRes private val errorColorAttr: Int = com.google.android.material.R.attr.colorError,
@AttrRes private val idleColorAttr: Int = com.google.android.material.R.attr.colorPrimary,
) {
/**
* Gets the context of the [TextInputLayout].
*/
private val context: Context
get() = textInputLayout.context
/**
* Sets the [TextInputLayout] to success mode with the specified success text.
*
* @param successText The text to be displayed in the success mode.
*/
fun setSuccessMode(successText: CharSequence?) {
textInputLayout.setError(successText)
textInputLayout.setErrorIconDrawable(R.drawable.ic_success)
val successColorStateList = getColorStateListFromAttr(successColorAttr)
textInputLayout.setErrorTextColor(successColorStateList)
textInputLayout.setHintTextColor(successColorStateList)
textInputLayout.setErrorIconTintList(successColorStateList)
textInputLayout.setBoxStrokeErrorColor(successColorStateList)
}
/**
* Sets the [TextInputLayout] to error mode with the specified error text.
*
* @param errorText The text to be displayed in the error mode.
*/
fun setErrorMode(errorText: CharSequence?) {
textInputLayout.setError(errorText)
textInputLayout.setErrorIconDrawable(R.drawable.ic_error)
val errorColorStateList = getColorStateListFromAttr(errorColorAttr)
textInputLayout.setErrorTextColor(errorColorStateList)
textInputLayout.setHintTextColor(errorColorStateList)
textInputLayout.setErrorIconTintList(errorColorStateList)
textInputLayout.setBoxStrokeErrorColor(errorColorStateList)
}
/**
* Sets the [TextInputLayout] to helper text mode with the specified helper text.
*
* @param helperText The text to be displayed in the helper text mode.
*/
fun setHelperTextMode(helperText: CharSequence?) {
textInputLayout.setError(null)
val hintColorStateList = getColorStateListFromAttr(idleColorAttr)
textInputLayout.setHintTextColor(hintColorStateList)
textInputLayout.setHelperText(helperText)
}
/**
* Retrieves a [ColorStateList] based on the specified theme attribute.
*
* @param attr The theme attribute to retrieve the color for.
* @return A [ColorStateList] based on the specified theme attribute.
*/
private fun getColorStateListFromAttr(
@AttrRes attr: Int
): ColorStateList? {
val colorResId = context.resolveColorResIdFromAttribute(attr)
return ContextCompat.getColorStateList(context, colorResId)
}
}
fun Context.resolveColorResIdFromAttribute(@AttrRes attrId: Int): Int {
var typedArray: TypedArray? = null
return try {
typedArray = obtainStyledAttributes(intArrayOf(attrId))
typedArray.getResourceId(0, 0)
} finally {
typedArray?.recycle()
}
}
I am unsure if this is the best way. And, I still kinda feel like I should do this using theming/styling/selectors, but I don't know if something like that is possible.
I didn't want to extend the TextInputLayout
in the end because this implementation can work without a custom class. This, also, saves me from managing the state of the view.
This is how it looks as of now (Material3):