Recently we finished learning the MVVM Design Pattern in Android App Development, and while learning that we used RESTful APIs to communicate with our Back End. But sometimes we do create server less application using Firebase. And some people asked that “How do I follow MVVM when using Firebase?“. That is why in this Firebase MVVM Example we will learn implementing the Firebase Authentication with MVVM Design Pattern.
Table of Contents
- 1 Prerequisites
- 2 Creating an Android Project
- 3 Adding Required Dependencies
- 4 Adding Firebase Authentication
- 5 Enabling Data Binding
- 6 Creating Packages
- 7 Creating Activities
- 8 Creating Firebase Source
- 9 Creating User Repository
- 10 Creating an AuthListener
- 11 Creating AuthViewModel and Factory
- 12 Designing UI and Binding Data
- 13 Functions for Switching Between Login and Home Activity
- 14 Creating Dependency Provider using Kodein
- 15 Binding ViewModel and Activities
- 16 Finishing Up Home Activity
- 17 Firebase MVVM Example Source Code
Prerequisites
If you are an absolute newbie and accidentally came here then, I would like to tell you that this post is not for you. Before moving ahead in this post you should already know these things.
If you don’t know these things, then take your time and go through the above courses first. Then you can follow this. And if you are brave enough to follow it without the above mention courses, then that’s awesome let’s start. And one thing I forgot, we also need a little bit of RxJava and RxAndroid.
Creating an Android Project
As always we need to create a new Android Studio Project. Here, I am creating a project named “Firebase Auth MVVM”. You can change the name if you want. But make sure you have the following settings while creating the project.
Once your project is loaded, we will start adding the required dependencies.
Adding Required Dependencies
First add the following in your app level build.gradle file. You need to add all these lines inside dependencies block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// ViewModel and LiveData implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" //New Material Design implementation 'com.google.android.material:material:1.1.0-alpha07' //Kodein Dependency Injection implementation "org.kodein.di:kodein-di-generic-jvm:6.2.1" implementation "org.kodein.di:kodein-di-framework-android-x:6.2.1" //RxJava implementation "io.reactivex.rxjava2:rxjava:2.2.5" implementation "io.reactivex.rxjava2:rxandroid:2.1.0" |
As you can see we are going to use ViewModel, LiveData, Material Design, Kodein Dependency Injection and RxJava. But other than all these dependencies, the most important thing that we need is the Firebase. And we did not add this yet. But don’t worry sync the project for now.
Adding Firebase Authentication
Now adding firebase is very easy and I already discussed it many times in videos and post. So I think no point of explaining the same thing again and again. You can go to Tools -> Firebase to add firebase authentication in Android Studio. Still you have confusion then you can just check this link where I added Firebase Cloud Messaging. But you don’t need to add cloud messaging, you need to add Authentication. So just select Authentication instead of Cloud Messaging and the process is same.
Enabling Data Binding
As we are going to implement the Model View ViewModel design pattern and data binding is a key point of this pattern. So let’s enable data binding.
- Again go to app level build.gradle file and enable data binding by adding following code inside android block.
1 2 3 4 5 |
dataBinding { enabled = true } |
- Â After this inside the file gradle.properties add this line at the bottom.
1 2 3 |
android.databinding.enableV2=true |
- Now finally sync your project and we are good to go.
Creating Packages
Organizing the codes in different packages properly is very important thing, if you don’t want to bang your head, when you need to open different files of your project. haha 😀 . So first we will create the following package structure in our Android Studio. Just to make the things well organized.
As you can see I have created thre main packages
- data: Inside this package we will store the models and the repositories. So again we have two more packages inside this package.
- ui: All the UI related things will go inside this package (Activities, Fragments, ViewModels, Adapters etc). Inside this package we have two more packages for the Auth and for the Home screen of our application.
- utils: Inside this package we will store the helper functions.
Now finally refactor your MainActivity as LoginActivity and activity_main.xml as activity_login.xml. Because the MainActivity is our LoginActivity and changing a sensible name is always a good idea. So refactor both xml and kotlin file for the activity and we are good to go.
Creating Activities
In this project we basically need three activities. For Login, for Signup and for Home. We already have the Login and we need to create the Home and the Signup Activity. So create empty activities in Auth and Home Package as shown in the below image.
So when the user is authenticated either from Login or Signup activity, we will display the HomeActivity.
According to the MVVM Design Rule, our View can communicate with ViewModel, ViewModel can communicate with Repository and Repository can communicate with our Back End, which is in this case Firebase.
So first we will create a Firebase class to handle Firebase operations.
Creating Firebase Source
- Inside your data package create one more package named firebase. And inside this package create a class named FirebaseSource.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class FirebaseSource { private val firebaseAuth: FirebaseAuth by lazy { FirebaseAuth.getInstance() } fun login(email: String, password: String) = Completable.create { emitter -> firebaseAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener { if (!emitter.isDisposed) { if (it.isSuccessful) emitter.onComplete() else emitter.onError(it.exception!!) } } } fun register(email: String, password: String) = Completable.create { emitter -> firebaseAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener { if (!emitter.isDisposed) { if (it.isSuccessful) emitter.onComplete() else emitter.onError(it.exception!!) } } } fun logout() = firebaseAuth.signOut() fun currentUser() = firebaseAuth.currentUser } |
- As you can see in the above class we have 3 functions, for login, signup, logout and getting the currently logged in user. The code is pretty much self explanatory, and it is almost the same as we do with Firebase. The only difference here is, we are using Completable that is an RxJava class. Completable basically means it holds something that will complete and we can get an indication when it is completed or failed. And it is the perfect class to use with FirebaseAuth because auth is a network operation that will complete and we need to know if it is completed or failed.
- So here we are creating a Completable and inside the completable we are performing the authentication. Once it is completed we are using the emitter to indicated that the task is completed or failed. Fairly simple, I guess.Â
So we have the FirebaseSource, and now we need a Repository so that we can communicate with the Firebase.
Creating User Repository
- Inside the repositories package (which is inside data) create a new class named UserRepository. To the constructor of this class we will pass the FirebaseSource that we just created. And then with the help of firebase source, we will perform the operations needed.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class UserRepository ( private val firebase: FirebaseSource ){ fun login(email: String, password: String) = firebase.login(email, password) fun register(email: String, password: String) = firebase.register(email, password) fun currentUser() = firebase.currentUser() fun logout() = firebase.logout() } |
- As you can see the code is very simple. We are just calling the functions, that we defined inside FirebaseSource.
- Now we have the repository that will communicate with our backend. And we need a ViewModel that can communicate with the Repository.
But we also need an AuthListener here so that when the task is finished we can fire a callback inside our Activity, to determine what happened to the login or signup operation.Â
Creating an AuthListener
- Inside the auth package (that is inside ui) create an interface named AuthListener.
1 2 3 4 5 6 7 |
interface AuthListener { fun onStarted() fun onSuccess() fun onFailure(message: String) } |
- Now we will create our ViewModel.
Creating AuthViewModel and Factory
AuthViewModel
- Again inside the auth package create a new class named AuthViewModel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
class AuthViewModel( private val repository: UserRepository ) : ViewModel() { //email and password for the input var email: String? = null var password: String? = null //auth listener var authListener: AuthListener? = null //disposable to dispose the Completable private val disposables = CompositeDisposable() val user by lazy { repository.currentUser() } //function to perform login fun login() { //validating email and password if (email.isNullOrEmpty() || password.isNullOrEmpty()) { authListener?.onFailure("Invalid email or password") return } //authentication started authListener?.onStarted() //calling login from repository to perform the actual authentication val disposable = repository.login(email!!, password!!) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ //sending a success callback authListener?.onSuccess() }, { //sending a failure callback authListener?.onFailure(it.message!!) }) disposables.add(disposable) } //Doing same thing with signup fun signup() { if (email.isNullOrEmpty() || password.isNullOrEmpty()) { authListener?.onFailure("Please input all values") return } authListener?.onStarted() val disposable = repository.register(email!!, password!!) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ authListener?.onSuccess() }, { authListener?.onFailure(it.message!!) }) disposables.add(disposable) } fun goToSignup(view: View) { Intent(view.context, SignupActivity::class.java).also { view.context.startActivity(it) } } fun goToLogin(view: View) { Intent(view.context, LoginActivity::class.java).also { view.context.startActivity(it) } } //disposing the disposables override fun onCleared() { super.onCleared() disposables.dispose() } } |
- And we have the ViewModel, now we just need to use DataBinding with our Activity.
AuthViewModel Factory
- As we need the UserRepository inside the AuthViewModel we need a ViewModelFactory to generate the ViewModel with the required parameter.
- So create one more class named AuthViewModelFactory inside the auth package.
1 2 3 4 5 6 7 8 9 10 11 12 |
@Suppress("UNCHECKED_CAST") class AuthViewModelFactory( private val repository: UserRepository ) : ViewModelProvider.NewInstanceFactory() { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return AuthViewModel(repository) as T } } |
Designing UI and Binding Data
Login Activity
- Go inside your activity_login.xml and paste the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <data> <variable name="viewmodel" type="net.simplifiedcoding.ui.auth.AuthViewModel"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#008577" android:orientation="vertical" tools:context=".ui.auth.LoginActivity" android:scrollbarAlwaysDrawVerticalTrack="true"> <RelativeLayout android:id="@+id/loginLayout" android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/login_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:gravity="center_horizontal" android:text="Account Login" android:textColor="#fff" android:textSize="26sp" android:textStyle="bold"/> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/login_title" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="70dp" android:background="#fff" android:elevation="4dp" android:orientation="vertical" android:padding="20dp"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingTop="30dp"> <TextView android:labelFor="@id/text_email" android:text="email" android:fontFamily="sans-serif-light" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <EditText android:id="@+id/text_email" android:layout_width="fill_parent" android:layout_height="wrap_content" android:drawableLeft="@drawable/ic_email" android:drawablePadding="10dp" android:singleLine="true" android:text="@={viewmodel.email}" android:layout_marginBottom="10dp" android:hint="john@gmail.com" android:inputType="textEmailAddress"/> <TextView android:labelFor="@id/edit_text_password" android:text="password" android:fontFamily="sans-serif-light" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <EditText android:drawablePadding="10dp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:singleLine="true" android:text="@={viewmodel.password}" android:id="@+id/edit_text_password" android:drawableLeft="@drawable/ic_lock" android:hint="Password" android:inputType="textPassword"/> <TextView android:id="@+id/text_view_forget_password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:paddingTop="5dp" android:text="Forgot Password?"/> <Button android:id="@+id/button_sign_in" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="22dp" android:onClick="@{() -> viewmodel.login()}" android:background="#d67601" android:text="Log in" android:textAllCaps="false" android:textColor="#fff" android:textSize="18sp"/> <TextView android:id="@+id/text_view_register" android:layout_gravity="center_horizontal" android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:textAlignment="center" android:onClick="@{(v) -> viewmodel.goToSignup(v)}" android:text="Don't have an account.\nRegister Here" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </RelativeLayout> <ImageButton android:id="@+id/user_profile_photo" android:layout_width="100dp" android:layout_height="100dp" android:layout_below="@+id/login_title" android:layout_centerHorizontal="true" android:layout_marginTop="16dp" android:background="@drawable/user_profile_image_background" android:elevation="4dp" android:src="@drawable/ic_boy"/> </RelativeLayout> <ProgressBar android:id="@+id/progressbar" android:visibility="gone" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout> </layout> |
- The above XML code will generate the following layout.
But I have used some drawable resources to design the above layout. And after copying and pasting you will see some error that those resources are missing. But don’t worry you can get my source code and all the resources at the end of this post.Â
Also make sure inside the data tag, you have given your ViewModel path according to your project.Â
Signup Activity
Now we will do pretty much the same thing in SignupActivity as well.
- First copy the following XML inside activity_signup.xml.Â
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <data> <variable name="viewmodel" type="net.simplifiedcoding.ui.auth.AuthViewModel"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#008577" android:orientation="vertical" tools:context=".ui.auth.SignupActivity" android:scrollbarAlwaysDrawVerticalTrack="true"> <RelativeLayout android:id="@+id/signupLayout" android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/login_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:gravity="center_horizontal" android:text="New User Signup" android:textColor="#fff" android:textSize="26sp" android:textStyle="bold"/> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/login_title" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="70dp" android:background="#fff" android:elevation="4dp" android:orientation="vertical" android:padding="20dp"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingTop="30dp"> <TextView android:labelFor="@id/text_email" android:text="email" android:fontFamily="sans-serif-light" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <EditText android:id="@+id/text_email" android:layout_width="fill_parent" android:layout_height="wrap_content" android:drawableLeft="@drawable/ic_email" android:drawablePadding="10dp" android:singleLine="true" android:text="@={viewmodel.email}" android:layout_marginBottom="10dp" android:hint="john@gmail.com" android:inputType="textEmailAddress"/> <TextView android:labelFor="@id/edit_text_password" android:text="password" android:fontFamily="sans-serif-light" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <EditText android:drawablePadding="10dp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:singleLine="true" android:text="@={viewmodel.password}" android:id="@+id/edit_text_password" android:drawableLeft="@drawable/ic_lock" android:hint="Password" android:inputType="textPassword"/> <Button android:id="@+id/button_sign_up" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="22dp" android:onClick="@{() -> viewmodel.signup()}" android:background="#d67601" android:text="Sign Up" android:textAllCaps="false" android:textColor="#fff" android:textSize="18sp"/> <TextView android:id="@+id/text_view_register" android:layout_gravity="center_horizontal" android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:textAlignment="center" android:onClick="@{(v) -> viewmodel.goToLogin(v)}" android:text="Already have an account.\nLogin Here" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </RelativeLayout> <ImageButton android:id="@+id/user_profile_photo" android:layout_width="100dp" android:layout_height="100dp" android:layout_below="@+id/login_title" android:layout_centerHorizontal="true" android:layout_marginTop="16dp" android:background="@drawable/user_profile_image_background" android:elevation="4dp" android:src="@drawable/ic_boy"/> </RelativeLayout> <ProgressBar android:id="@+id/progressbar" android:visibility="gone" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout> </layout> |
- This XML will generate the following design.
Again make sure that you have defined the correct path of the ViewModel inside the variable tag.Â
We have the design ready, and now we just need to bind the ViewModel and View. But before doing this thing we need one more small thing. When the authentication is successful we need to start the HomeActivity and when the user logs out again we need to start the LoginActivity. And we don’t want to repeat the codes again and again, so for both these operation we will define two extension functions.
Functions for Switching Between Login and Home Activity
- Create a new kotlin file inside util package and name it ViewUtils.kt.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
fun Context.startHomeActivity() = Intent(this, HomeActivity::class.java).also { it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK startActivity(it) } fun Context.startLoginActivity() = Intent(this, LoginActivity::class.java).also { it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK startActivity(it) } |
- We will use these functions for login and logout.
- Now we can bind the ViewModel and Activites but before doing that we will use Kodein to create a Dependency Provider.
Creating Dependency Provider using Kodein
As you know, we should not make the classes dependent. And to make the classes independent we need to use the Dependency Injection pattern. And to do this we will be using the Kodein Framework that we already added.
- So inside your main package create a new class named FirebaseApplication.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class FirebaseApplication : Application(), KodeinAware{ override val kodein = Kodein.lazy { import(androidXModule(this@FirebaseApplication)) bind() from singleton { FirebaseSource()} bind() from singleton { UserRepository(instance()) } bind() from provider { AuthViewModelFactory(instance()) } bind() from provider { HomeViewModelFactory(instance()) } } } |
- Now we need to define this class in our AndroidManifest.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="net.simplifiedcoding"> <application android:name=".FirebaseApplication" 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/AppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> <activity android:name=".ui.home.HomeActivity"> </activity> <activity android:name=".ui.auth.SignupActivity"> </activity> <activity android:name=".ui.auth.LoginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> |
- And we are done.
Binding ViewModel and Activities
LoginActivity
- Open LoginActivity and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class LoginActivity : AppCompatActivity(), AuthListener, KodeinAware { override val kodein by kodein() private val factory : AuthViewModelFactory by instance() private lateinit var viewModel: AuthViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityLoginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login) viewModel = ViewModelProviders.of(this, factory).get(AuthViewModel::class.java) binding.viewmodel = viewModel viewModel.authListener = this } override fun onStarted() { progressbar.visibility = View.VISIBLE } override fun onSuccess() { progressbar.visibility = View.GONE startHomeActivity() } override fun onFailure(message: String) { progressbar.visibility = View.GONE Toast.makeText(this, message, Toast.LENGTH_SHORT).show() } override fun onStart() { super.onStart() viewModel.user?.let { startHomeActivity() } } } |
- The code is very simple and straightforward, in case you are having trouble understanding anything, please comment.
Signup Activity
- Now we will do the same thing inside SignupActivity as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
class SignupActivity : AppCompatActivity(), AuthListener, KodeinAware { override val kodein by kodein() private val factory : AuthViewModelFactory by instance() private lateinit var viewModel: AuthViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_signup) val binding: ActivitySignupBinding = DataBindingUtil.setContentView(this, R.layout.activity_signup) viewModel = ViewModelProviders.of(this, factory).get(AuthViewModel::class.java) binding.viewmodel = viewModel viewModel.authListener = this } override fun onStarted() { progressbar.visibility = View.VISIBLE Intent(this, HomeActivity::class.java).also { it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK startActivity(it) } } override fun onSuccess() { progressbar.visibility = View.GONE startHomeActivity() } override fun onFailure(message: String) { progressbar.visibility = View.GONE Toast.makeText(this, message, Toast.LENGTH_SHORT).show() } } |
- Now we just need to work on HomeActivity.
Finishing Up Home Activity
Creating HomeViewModel and HomeViewModelFactory
- First create HomeViewModel inside the home package.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class HomeViewModel( private val repository: UserRepository ) : ViewModel() { val user by lazy { repository.currentUser() } fun logout(view: View){ repository.logout() view.context.startLoginActivity() } } |
- Again we need to create our ViewModelFactory because we are passing repository to the HomeViewModel. So create one more class named HomeViewModelFactory.
1 2 3 4 5 6 7 8 9 10 11 12 |
@Suppress("UNCHECKED_CAST") class HomeViewModelFactory( private val repository: UserRepository ) : ViewModelProvider.NewInstanceFactory() { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return HomeViewModel(repository) as T } } |
- And that’s it. Now let’s code the XML part of our HomeActivity.
Designing UI
- Go inside activity_home.xml and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewmodel" type="net.simplifiedcoding.ui.home.HomeViewModel"/> </data> <RelativeLayout android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.home.HomeActivity"> <TextView android:text="@{viewmodel.user.email}" tools:text="probelalkhan@gmail.com" android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:layout_centerInParent="true" android:id="@+id/textViewEmail" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:onClick="@{(v) -> viewmodel.logout(v)}" android:text="Logout" android:layout_centerHorizontal="true" android:layout_below="@id/textViewEmail" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout> </layout> |
- Again make sure you have given the correct path of the HomeViewModel inside the variable tag.Â
Binding HomeViewModel and HomeActivity
- Again we will do the same thing, that we did for Login and Signup activity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class HomeActivity : AppCompatActivity(), KodeinAware { override val kodein by kodein() private val factory : HomeViewModelFactory by instance() private lateinit var viewModel: HomeViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) val binding: ActivityHomeBinding = DataBindingUtil.setContentView(this, R.layout.activity_home) viewModel = ViewModelProviders.of(this, factory).get(HomeViewModel::class.java) binding.viewmodel = viewModel } } |
- And we are done. You can try running the application and it should work fine. For example you can check my APK to test, if yours is not working.
Firebase MVVM Example Source Code
In case you are not able to follow through the tutorial, don’t worry you have my source code to cross check your project. And if still having problem comment below and I will try to help you out.
So that is all for this Firebase MVVM Example friends. I hope I taught you something new with this post. And if you think it is really useful then please support me by sharing this post with your friends. Thank You 🙂