Arpan Sarkar
07/31/2021, 8:18 PMLiveData to how can i convert the MediatorLiveData part to StateFlow
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
abstract class InputFrom {
private val _isFromValid = MediatorLiveData<Boolean>()
val isFromValid: LiveData<Boolean>
get() = _isFromValid
abstract val fields: List<LiveInputField<*>>
fun init() {
fields.forEach { inputField ->
_isFromValid.addSource(inputField.value) { changedValue ->
inputField.validate(changedValue)
_isFromValid.value = fields.all {
it.isValid
}
}
}
}
inner class LiveInputField<T>(
private val errorMessage: String? = null,
private val predicate: (T?) -> Boolean
) {
val value = MutableLiveData<T>()
private val _error = MutableLiveData<String>()
val errorText: LiveData<String>
get() = _error
var isValid: Boolean = false
fun validate(value: Any?) {
@Suppress("UNCHECKED_CAST")
return if (predicate(value as? T)) {
_error.value = null
isValid = true
} else {
_error.value = errorMessage
isValid = false
}
}
}
}
Example Usages:
class LoginFrom : InputFrom() {
val username =
LiveInputField<String>("At least 4 characters, only letters, numbers, ., _ allowed.") {
!it.isNullOrBlank() &&
Pattern.compile("[a-zA-Z0-9_.]{4,}")
.matcher(it)
.matches()
}
val password =
LiveInputField<String>(
"8 characters or more, at least one number, one uppercase, one lowercase."
) {
!it.isNullOrBlank()/* &&
Pattern.compile("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,})")
.matcher(it)
.matches()*/
}
override val fields: List<LiveInputField<*>>
get() = listOf(
username,
password
)
}Nick Allen
08/02/2021, 10:31 PMmyInputFrom.isFromValid.asFlow().stateIn(someScope) or something more like combine(fields.map { field -> field.value.asFlow().map { field.validate() } }) { validateResults -> validateResults.all { it } }. (second snippet would benefit from breaking out some helper methods.
Honestly, when I look at LiveInputField, I feel like I'd rather see something more like:
class LiveInputField<T>(
private val errorMessage: String? = null,
private val predicate: (T?) -> Boolean
) {
private val _value = MutableStateFlow<T>()
val validOrError: Flow<ValueOrError<T>> = _value
.map {
if (predicate(it) ValidOrError.Valid(it)
else ValidOrError.Error(errorMessage)
}
}
sealed class ValidOrError<T> {
data class Valid(val value: T)
data class Error(val message: String?)
}
You could do the same thing with MutableLiveData and MediatorLiveData. The idea is just to expose the validation result itself instead of exposing the pieces and then asking the class's owner to put all the pieces together.