ViewModel — — A Simple Login using viewmodel Android in Kotlin

Ashwini Jadhav
3 min readOct 30, 2020

In this tutorial, we are going to see how to use viewmodel for login in your Android application.

ViewModel: It is a bridge between the View and Model(business logic). It does not have any clue which View has to use it as it does not have a direct reference to the View. So basically, the ViewModel should not be aware of the view who is interacting with. It interacts with the Model and exposes the observable that can be observed by the View.

  1. Go to File ⇒ New Project. .select Empty Activity and proceed.
  2. Add dependencies : — -
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 30
buildToolsVersion "30.0.2"

defaultConfig {
applicationId "com.example.viewmodelimp"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

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 "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "android.arch.lifecycle:viewmodel:1.1.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation 'com.github.Zhuinden:live-event:1.0.0'
implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.2.5"
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
//////build.gradle (project Module)buildscript {
ext.kotlin_version = '1.3.61'
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

3.activity_main: — — -

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint"
android:layout_width="match_parent"
android:layout_height="200dp">

<TextView
android:id="@+id/loginlogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login"
android:textColor="#000000"
android:textSize="40sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:layout_marginTop="148px"
android:padding="8dp"
android:layout_below="@id/constraint"
android:id="@+id/loginuser"
android:hint="Username"
android:drawablePadding="10dp"

/>



/>
<EditText
android:drawablePadding="10dp"

android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:padding="8dp"
android:layout_marginTop="15dp"
android:layout_below="@id/loginuser"
android:id="@+id/loginpassword"
android:hint="Password"
android:inputType="textPassword"

/>

<Button
android:id="@+id/loginbtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/loginpassword"
android:layout_marginLeft="50dp"
android:layout_marginTop="15dp"
android:layout_marginRight="50dp"
android:background="@android:color/white"
android:shadowColor="@android:color/white"
android:text="LOGIN"
android:textSize="25sp" />


</RelativeLayout>

4.LoginActivity: — -

class LoginActivity : AppCompatActivity() {
private val viewModel by viewModels<UserViewModel>()

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

loginuser.onTextChanged {
viewModel.user.value = it.toString()
}

loginpassword.onTextChanged {
viewModel.password.value = it.toString()
}

loginbtn.setOnClickListener {
viewModel.login1()
}

viewModel.loginResult.observe(this) { result ->
when (result) {
UserViewModel.LoginResult.UserMissing -> {
Toast.makeText(
applicationContext, "Data is missing", Toast.LENGTH_LONG
).show()
loginuser.error = "Email required"
loginuser.requestFocus()
}
UserViewModel.LoginResult.PasswordMissing -> {
loginpassword.error = "Password required"
loginpassword.requestFocus()
}
UserViewModel.LoginResult.NetworkFailure -> {
//do anything
}

UserViewModel.LoginResult.Success -> {
val intent = Intent(applicationContext, HomeActivity::class.java)
intent.flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

startActivity(intent)
finish()
}
else -> {
Toast.makeText(applicationContext, "hello", Toast.LENGTH_LONG).show()
}
}
}
}

inline fun EditText.onTextChanged(crossinline textChangeListener: (String) -> Unit) {
addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable) {
textChangeListener(editable.toString())
}

override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
}

5.UserViewModel: — —

class UserViewModel(context: Application, savedStateHandle: SavedStateHandle) : AndroidViewModel(
context
) {
sealed class LoginResult {
object UserMissing : LoginResult()

object PasswordMissing : LoginResult()

class NetworkError(val userMessage: String) : LoginResult()

object NetworkFailure : LoginResult()

object Success : LoginResult()
}


val user: MutableLiveData<String> = savedStateHandle.getLiveData("user", "")
val password: MutableLiveData<String> = savedStateHandle.getLiveData("password", "")


private val loginResultEmitter = EventEmitter<LoginResult>()
val loginResult: EventSource<LoginResult> = loginResultEmitter


fun login1() {
val email = user.value!!.toString().trim()
val password = password.value!!.toString().trim()

if (email.isEmpty()) {
loginResultEmitter.emit(LoginResult.UserMissing)
return
}


if (password.isEmpty()) {
loginResultEmitter.emit(LoginResult.PasswordMissing)
return
}
loginResultEmitter.emit(LoginResult.Success)


}
}

6: finally HomeActivity

class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.home)
}
}

7.home.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="welcome"
android:textColor="@android:color/black"
android:textSize="70dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

8.Manifest: — -

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.viewmodelimp">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ViewModelImp">
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".HomeActivity"/>
</application>

</manifest>

9:output/Screenshots: — -

1.login screen
2.home screen

--

--