OOZOU
Get in Touch
Back to Blog

Modern Android Form Validations With Data Binding

How to validate form fields without if or when expressions

December 21, 2020
3 min read
Modern Android Form Validations With Data Binding

Introduction

One of the many things you need to know how to do as an android developer is creating forms. With that comes the responsibility of client side validation. There are various approaches used to do this but I'd like to share my personal favourite with you all.

Let's Get Into It

First we will be creating a validator class that will handle validation of liveData

//A predicate is a function that evaluates to true when its param matches the condition of the predicate
typealias Predicate = (value: String?) -> Boolean

class LiveDataValidator(private val liveData: LiveData<String>) {
    private val validationRules = <em>mutableListOf</em><Predicate>()
    private val errorMessages = <em>mutableListOf</em><String>()

     var error = MutableLiveData<String?>()

    <strong>//For checking if the liveData value matches the error condition set in the validation rule predicate
    //The livedata value is said to be valid when its value doesn't match an error condition set in the predicate</strong>
    fun isValid(): Boolean {
        for (i in 0 <em>until </em>validationRules.size) {
            if (validationRules[i](liveData.<em>value</em>)) {
                emitErrorMessage(errorMessages[i])
                return false
            }
        }

        emitErrorMessage(null)
        return true
    }

    <strong>//For emitting error messages</strong>
    private fun emitErrorMessage(messageRes: String?) {
        error.<em>value </em>= messageRes
    }

    <strong>//For adding validation rules</strong>
    fun addRule(errorMsg: String, predicate: Predicate) {
        validationRules.add(predicate)
        errorMessages.add(errorMsg)
    }
}

Then, we will create a ValidatorResolver which will help us aggregate multiple liveData validators. This will be useful when we want to check if all liveData attached to our form are in a valid state.

class LiveDataValidatorResolver(private val validators: List<LiveDataValidator>) {
    fun isValid(): Boolean {
        for (validator in validators) {
            if (!validator.isValid()) return false
        }
        return true
    }
}

Next, we will create the validation rules in our viewModel

class ExampleViewModel : ViewModel() {
   val usernameLiveData = MutableLiveData<String>()
   val usernameValidator = LiveDataValidator(usernameLiveData).<em>apply </em><strong>{
      //Whenever the condition of the predicate is true, the error message should be emitted
      </strong>addRule("username is required") <strong>{ it</strong>.<em>isNullOrBlank</em>() <strong>}
   }
   </strong>val passwordLiveData = MutableLiveData<String>()
   val passwordValidator = LiveDataValidator(passwordLiveData).<em>apply </em><strong>{
      //We can add multiple rules. 
      //However the order in which they are added matters because the rules are checked one after the other
      </strong>addRule("password is required") <strong>{ it</strong>.<em>isNullOrBlank</em>() <strong>}
      </strong>addRule("password length must be 10") <strong>{ it?.length != 10</strong> <strong>}
   }

   //We will use a mediator so we can update the error state of our form fields
   //and the enabled state of our login button as the form data changes
   </strong>val isLoginFormValidMediator = MediatorLiveData<Boolean>()

   init {
       isLoginFormValidMediator.<em>value </em>= false
       isLoginFormValidMediator.addSource(usernameLiveData) <strong>{ </strong>validateForm() <strong>}
       </strong>isLoginFormValidMediator.addSource(passwordLiveData) <strong>{ </strong>validateForm() <strong>}</strong>
   }

   //This is called whenever the usernameLiveData and passwordLiveData changes
   fun validateForm() {
       val validators = <em>listOf</em>(usernameValidator, passwordValidator)
       val validatorResolver = LiveDataValidatorResolver(validators)
       isLoginFormValidMediator.<em>value </em>= validatorResolver.isValid()
   }
}

Finally, we will be creating a simple login form containing two fields - username, password - and a button to complete the login process. The XML below assumes you're familiar with data binding.

<androidx.constraintlayout.widget.ConstraintLayout
<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/field_username"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:errorEnabled="true"
    <strong>app:error="@{viewModel.usernameValidator.error}"</strong>
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        <strong>android:text="@={viewModel.usernameLiveData}"</strong> />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/field_password"
    style="@style/TextInputLayoutTheme"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:errorEnabled="true"
    <strong>app:error="@{viewModel.passwordValidator.error}"</strong>
    app:layout_constraintEnd_toEndOf="parent"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toBottomOf="@+id/field_username">

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        <strong>android:text="@={viewModel.passwordLiveData}"</strong> />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.button.MaterialButton
    android:id="@+id/button_log_in"
    android:layout_width="match_parent"
    android:layout_height="60dp"
     android:layout_marginTop="100dp"
    <strong>android:enabled="@{viewModel.isLoginFormValidMediator}"</strong>
    android:text="Log in"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/field_password"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<br>

Conclusion

I prefer this method because it helps me avoid writing long if or when expressions whenever I have a huge form with plenty of validation rules. If you like flexing your creative muscles, you can give it a try in your next project and consider joining our team at Oozou.

Related Articles

How Can I Design a Software: A Beginners Guide for Business Owners

How Can I Design a Software: A Beginners Guide for Business Owners

Designing software is an exciting yet complex process that involves creativity, logical thinking, and problem-solving skills.

Read More →
The Future of Edge Computing: What Businesses Need to Know

The Future of Edge Computing: What Businesses Need to Know

The digital landscape is evolving rapidly, and businesses must adapt to keep up with rising demands for speed, efficiency, and real-time processing.

Read More →
The Future of Retail: AI, AR, and Digital Transformation Trends

The Future of Retail: AI, AR, and Digital Transformation Trends

Retail is evolving at an unprecedented pace, driven by AI (Artificial Intelligence), AR (Augmented Reality), and digital transformation technologies, revolutionizing the retail industry. Traditional brick-and-mortar stores are no longer just about selling products—they are becoming smart, interactiv

Read More →

Have a Project in Mind?

Let's discuss how we can help bring your ideas to life with our expertise in web and mobile development.

Start a Conversationhello@oozou.com
OOZOU Logo

Bangkok · Singapore · Hong Kong

X
Facebook
Instagram
LinkedIn

Services

  • Web Development
  • AI Agents & Generative AI
  • Mobile App Development
  • Data Analytics & Engineering
  • UI/UX & Product Design
  • Digital Transformation

Company

  • About Us
  • Careers
  • Blog
  • Whitepapers
  • Case Studies
  • Today I Learned
  • Contact

More

  • Industries
  • Get an Estimate
  • Partner Network
© 2026 OOZOU. All rights reserved.
Privacy PolicyCode of ConductABAC Policy