Android Firebase Authentication using Kotlin, MVVM, LiveData, and View Binding (part IV)
Part IV: User login using Firebase Authentication
Welcome to the fourth part of our Android Firebase Authentication series!
This post is part of a longer series:
- Part I: Introduction to Firebase Authentication
- Part II: Setting up Firebase project and integrating with Android Studio
- Part III: User registration using Firebase Authentication
- Part IV: User login using Firebase Authentication (current post)
- Part V: Implementing logout functionality using Firebase Authentication
- Part VI: Handling authentication state changes using Firebase Authentication
In the previous post, we learned how to create a user registration process using Firebase Authentication. In this post, we will learn how to implement user login functionality using Firebase Authentication.
A login screen is the first thing a user sees when opening your app. It is important to make the login process as easy and user-friendly as possible. Firebase Authentication provides an easy-to-use API for implementing secure login functionality. Let’s get started!
Creating the user login layout using View Binding
The first step is to create the user login layout. We will be using View Binding to access the views in the layout file. Here is an example of a simple user login layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<EditText
android:id="@+id/editTextEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"/>
<EditText
android:id="@+id/editTextPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"/>
<Button
android:id="@+id/buttonLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login"/>
</LinearLayout>
In your LoginActivity, add the following code to inflate the layout and access the views:
private lateinit var binding: ActivityLoginBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
}
Implementing the login functionality using Firebase Authentication
Now that we have the layout set up, let’s implement the login functionality using Firebase Authentication. Here is an example of how to do this:
binding.buttonLogin.setOnClickListener {
val email = binding.editTextEmail.text.toString()
val password = binding.editTextPassword.text.toString()
if (email.isNotEmpty() && password.isNotEmpty()) {
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Login successful
} else {
// Login failed
}
}
} else {
Toast.makeText(this, "Please enter email and password.", Toast.LENGTH_SHORT).show()
}
}
Updating the LiveData
To provide feedback to the user about the success or failure of the login operation, we will again use LiveData. By observing a LiveData object in the login activity, we can update the UI with relevant messages when the login process finishes.
First, we need to define a LiveData object that will hold the login result. In the ViewModel class of the login activity, we can create a MutableLiveData object of type LoginResult:
private val _loginResult = MutableLiveData<LoginResult>()
val loginResult: LiveData<LoginResult> = _loginResult
In the login function of the ViewModel, we can update the value of the _loginResult object depending on the result of the authentication:
fun login(email: String, password: String) {
// ...
.addOnCompleteListener { task ->
if (task.isSuccessful) {
// Login successful, update the login result
_loginResult.value = LoginResult(success = true)
} else {
// Login failed, update the login result
_loginResult.value = LoginResult(error = R.string.login_failed)
}
}
}
Note that the LoginResult is a custom data class that we define to hold the result of the login operation, including a boolean flag to indicate success or failure, and an optional error message. In this example, we use a string resource to display the error message to the user.
Finally, in the login activity, we can observe the loginResult LiveData object and update the UI accordingly. For example, we can show a Toast message when the login is successful, or display an error message in a TextView when the login fails:
viewModel.loginResult.observe(this, { loginResult ->
loginResult ?: return@observe // Ignore null values
if (loginResult.success) {
Toast.makeText(this, "Login successful!", Toast.LENGTH_SHORT).show()
// Navigate to the main activity
} else {
val errorMessage = loginResult.error ?: R.string.unknown_error
messageTextView.text = getString(errorMessage)
}
})
By using LiveData to communicate the result of the login operation between the ViewModel and the activity, we can easily update the UI with relevant feedback and avoid coupling between the presentation logic and the authentication logic.
Now that we have implemented both user registration and user login functionalities using Firebase Authentication, we can move on to the next step, which is implementing the logout functionality. In the next blog post, we will go through the process of adding a logout button to the user interface, implementing the logout functionality using Firebase Authentication, and updating the UI after logout using LiveData.
Next post - Part V: Implementing logout functionality using Firebase Authentication (not available yet)