What is ViewModel and How to design and implement Login Screen with ViewModel Pattern using Kotlin.

Last updated Jan 07, 2022

In this android example tutorial, we will see What is ViewModel and how to implement viewmodel pattern in Android Login Screen Kotlin code.

What is ViewModel?
In Android, the ViewModel class is intended to store and handle UI-related data in a lifecycle-aware manner. The ViewModel class enables data to persist via configuration changes such as screen rotations.

ViewModel Implementation in Login Screen:

Step 1: Create a new Project in android studio.

Go to File > New > New Project > Empty Activity > Next > Enter Name > Select Language Kotlin > Finish


Step 2: Open build.gradle(app) file and add the following code

Add ViewModel and LiveData Lifecycle dependency inside dependencies..

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"

 

Add the kotlin-kapt inside plugins

 id 'kotlin-kapt'

 

Enable the DataBinding inside android...

buildFeatures{
        dataBinding true
    }

 

Step 3: Create LoginUser Model
Create a new kotlin file (LoginUser.kt) and add the following code:

class User(private var email: String, private var password: String) : BaseObservable() {
    fun isDataValid(): Int {
        if (TextUtils.isEmpty(getEmail()))
            return 0
        else if (!Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches())
            return 1
        else if (getPassword().length < 6)
            return 2
        else
            return -1
    }

    fun getPassword(): String {
        return password
    }

    fun getEmail(): String {
        return email
    }

    fun setEmail(email: String) {
        this.email = email
    }

    fun setPassword(password: String) {
        this.password = password
    }

}

 

Step 4: Create a Interface
Create a new kotlin file (LoginResultCallBacks.kt) which has two function onSuccess and onError be like:

interface LoginResultCallBacks {
    fun onSuccess(message: String)
    fun onError(message: String)
}


Step 5: Implement ViewModel 

 Create a new kotlin file (LoginViewModel.kt) and add the following code:

class LoginViewModel(private val listener: LoginResultCallBacks) : ViewModel() {
    private val user: LoginUser

    init {
        this.user = LoginUser("", "")
    }

    val emailTextWatcher: TextWatcher
        get() = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                user.setEmail(s.toString())
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                // Code what you want to show before edit the email box
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // Code what you want to show on text Changed in email box

            }

        }


    //create function to set Password after user finish enter text
    val passwordTextWatcher: TextWatcher
        get() = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                user.setPassword(s.toString())
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                // Code what you want to show before edit the password box

            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // Code what you want to show on text Changed in password box

            }

        }

    fun onLoginClicked(v: View) {
        val loginCode: Int = user.isDataValid()
        if (loginCode == 0)
            listener.onError("Enter Email ID")
        else if (loginCode == 1)
            listener.onError("Invalid Email")
        else if (loginCode == 2)
            listener.onError("Password must be greater than 5")
        else
            listener.onSuccess("Success")
    }

}



TextWatcher: The Android Developer API has a useful class called TextWatcher. It can be used to keep an eye on an input text field and rapidly update data in other views. It can be handy for immediately counting the number of characters input in the text field and measuring password strength when entering, among other things.

Methods of TextWatcher

abstract void

afterTextChanged(Editable s)
This method is called to notify you that, somewhere within s, the text has been changed.

abstract void

beforeTextChanged(CharSequence s, int start, int count, int after)
This method is called to notify you that, within s, the count characters beginning at start are about to be replaced by new text with length after.

abstract void

onTextChanged(CharSequence s, int start, int before, int count)
This method is called to notify you that, within s, the count characters beginning at start have just replaced old text that had length before.

 

Create a new file of Kotlin (LoginViewModelFactory.kt) and add the following code:

We need to pass some input data to the constructor of the viewModel , we need to create a factory class for viewModel.

class LoginViewModelFactory (private val listener: LoginResultCallBacks):ViewModelProvider.NewInstanceFactory() {

    override fun create(modelClass: Class): T {
        return LoginViewModel(listener) as T
    }
}

 


Step 6: Open activity_main.xml file and add the following xml code.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.nishajain.loginregisterwithviewmodel.LoginViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:padding="20dp"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:text="Login"
            android:textSize="29sp"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/etEmail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter Email"
            app:addTextChangedListener="@{viewModel.emailTextWatcher}"></EditText>

        <EditText
            android:id="@+id/etPassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:hint="Enter Password"
            app:addTextChangedListener="@{viewModel.passwordTextWatcher}"></EditText>

        <Button
            android:id="@+id/loginBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:onClick="@{viewModel::onLoginClicked}"
            android:text="Login"></Button>
    </LinearLayout>
</layout>

 

Above layout file gives you output as below:

Jetpack Android View Model Login page

 

Step 7: Go to MainActivity.kt file 

Bind the view with activity, add the following code below setContentView(R.layout.activity_main).

val activityMainBinding = DataBindingUtil.setContentView activityMainBinding.viewModel = ViewModelProviders.of(this,LoginViewModelFactory(this)).get(LoginViewModel::class.java)

 

Implement the LoginResultCallBacks Interface in MainActivity.kt file

class MainActivity : AppCompatActivity(), LoginResultCallBacks {

 

Implement the methods of LoginResultCallBacks onSuccess() and onError()

override fun onSuccess(message: String) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }

    override fun onError(message: String) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }

 

Final Code of MainActivity.kt file,

class MainActivity : AppCompatActivity(), LoginResultCallBacks {
    override fun onSuccess(message: String) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }

    override fun onError(message: String) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)
        activityMainBinding.viewModel = ViewModelProviders.of(this, LoginViewModelFactory(this))
            .get(LoginViewModel::class.java)
    }
}

 


Step 8: Now run the app in your emulator or real device, you will get the given output:

When the field is empty:

Jetpack Android View Model Login page 2


When we typed wrong email address:

 

Jetpack Android View Model Login page 3


When we entered password value less than 6 digits:

 

Jetpack Android View Model Login page 4


When everthing is Okay then it shows you success message:

Jetpack Android View Model Login page 5

 

Complete Source Code of Login with ViewModel Example:

activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.nishajain.loginregisterwithviewmodel.LoginViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:padding="20dp"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:text="Login"
            android:textSize="29sp"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/etEmail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter Email"
            app:addTextChangedListener="@{viewModel.emailTextWatcher}"></EditText>

        <EditText
            android:id="@+id/etPassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:hint="Enter Password"
            app:addTextChangedListener="@{viewModel.passwordTextWatcher}"></EditText>

        <Button
            android:id="@+id/loginBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:onClick="@{viewModel::onLoginClicked}"
            android:text="Login"></Button>
    </LinearLayout>
</layout>

 

MainActivity.kt file

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.nishajain.loginregisterwithviewmodel.databinding.ActivityMainBinding
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProviders

class MainActivity : AppCompatActivity(), LoginResultCallBacks {
    override fun onSuccess(message: String) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }

    override fun onError(message: String) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)
        activityMainBinding.viewModel = ViewModelProviders.of(this, LoginViewModelFactory(this))
            .get(LoginViewModel::class.java)
    }
}

 

LoginUser.kt file

import android.text.TextUtils
import android.util.Patterns
import androidx.databinding.BaseObservable


class LoginUser(private var email: String, private var password: String) : BaseObservable() {
    fun isDataValid(): Int {
        if (TextUtils.isEmpty(getEmail()))
            return 0
        else if (!Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches())
            return 1
        else if (getPassword().length < 6)
            return 2
        else
            return -1
    }

    fun getPassword(): String {
        return password
    }

    fun getEmail(): String {
        return email
    }

    fun setEmail(email: String) {
        this.email = email
    }

    fun setPassword(password: String) {
        this.password = password
    }

}

 

LoginResultCallBacks.kt file

interface LoginResultCallBacks {
    fun onSuccess(message: String)
    fun onError(message: String)
}

 

LoginViewModel.kt file

import android.text.Editable
import android.text.TextWatcher
import android.view.View
import androidx.lifecycle.ViewModel

class LoginViewModel(private val listener: LoginResultCallBacks) : ViewModel() {
    private val user: LoginUser

    init {
        this.user = LoginUser("", "")
    }

    val emailTextWatcher: TextWatcher
        get() = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                user.setEmail(s.toString())
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                // Code what you want to show before edit the email box
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // Code what you want to show on text Changed in email box

            }

        }


    //create function to set Password after user finish enter text
    val passwordTextWatcher: TextWatcher
        get() = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                user.setPassword(s.toString())
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                // Code what you want to show before edit the password box

            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // Code what you want to show on text Changed in password box

            }

        }

    fun onLoginClicked(v: View) {
        val loginCode: Int = user.isDataValid()
        if (loginCode == 0)
            listener.onError("Enter Email ID")
        else if (loginCode == 1)
            listener.onError("Invalid Email")
        else if (loginCode == 2)
            listener.onError("Password must be greater than 5")
        else
            listener.onSuccess("Success")
    }

}

 

LoginViewModelFactory.kt file

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class LoginViewModelFactory (private val listener: LoginResultCallBacks):ViewModelProvider.NewInstanceFactory() {

    override fun create(modelClass: Class): T {
        return LoginViewModel(listener) as T
    }
}

 

build.gradle(app) file

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'

}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.nishajain.loginregisterwithviewmodel"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildFeatures{
        dataBinding true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
}

 

Conclusion: In this example we have covered what is Android Jetpack ViewModel and how to implement Login Screen with ViewModel in Android Studio using Kotlin Language.

 

Download Source code

 

Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

1084 Views