How to Create a Login-Register App with Kotlin using Fragments and Room Database [MVVM Architecture]

Sulu Mufi
11 min readDec 23, 2020

Let's look at the Steps.

  1. Adding Dependencies
  2. Setting up the Room DataBase
  3. Creating Repository
  4. Designing Fragment Layouts
  5. Describing the Navigation
  6. Integrating the Components with MVVM Architecture
  7. Creating Classes for Fragments, ViewModels, and ViewModelFactories
  8. Coding the Logic

1. Adding Dependencies

We’ll list out all the Dependencies required for the project

  1. View Model
  2. Live Data
  3. Annotation Processor
  4. Room
  5. Coroutines and coroutines Extensions for Room
  6. Enabling Data Binding

2. Setting Up the Room DataBase

Room is a persistence library, part of the Android Jetpack. Basically, the Room acts as a Layer on top of the SQLite Database.

The most notable advantages of using Room are

  1. Removing Boilerplate code
  2. Helps in Verifying SQL Queries.

Ok, let's dive in. Setting up a Database involves creating 3 Kotlin files.

  1. A Data Class — Defining the table and all the fields for the table
  2. A DAO Interface — Includes all the possible Queries used in the Project, using Annotations.
  3. An Abstract class — It contains the database holder and acts as the main access point for the underlying connection to your app’s persisted, relational data.

For more information on Room Check content below.

  1. Now let’s see the code for the Data class.

Here, we create a table called “Register_users_table” which contains the following fields.

  1. User Id [Int]— (As Primary Key and Autogenerated)
  2. FirstName [String]
  3. LastName [String]
  4. UserName [String]
  5. Password [Strign]

2) The Next Step is to create a Dao Interface with all the Possible Database Queries Required for the project.

@Insert:

“@Insert” is an Annotation that is readily available in the room, which allows you to pass in an Object of the Entity Class as an argument.

Here we pass in an Object of the “RegisterEntity” Class.

@Query:

“@Query” is an annotation that takes the custom database Queries.

Here we have 2 Query functions.

a) fun getAllUsers() — this function returns the entire table with all the data embedded in a LiveData list. The Main Advantage of returning a LiveData is that we Don’t have to call the function to Execute the Query Explicitly. It is automatically called every time there is a change made to the database

this is function is typically used to print out the data in the database. We’ll use it in the later stage of the project

b) suspend fun getUsername(userName: String): RegisterEntity? — this function takes the string as an argument and returns an Object of type “RegisterEntity”

This command compares the string received as the argument with the attribute UserName and returns all the records that belong to that particular username. If the username doesn't exist, it returns NULL

This function is used in 2 places

i) Check if the username already exists while registering. If not, return NULL

ii) Check if the username exists while Logging in and, if existing, receive all the user's records, including the password so that it can be compared with the password field's text input. If not, return NULL

2) Final step: Setting up the Database

This step is mostly Boilerplate, and no need to worry about complex code.

Here, we define an abstract class that extends RoomDatabase() and accompanies the @Data Annotation, which receives the DataClass defined earlier. i.e “RegisterEntity.”

Inside the abstract class, we have a Declaration of the Object Dao Interface as abstract.

The next part of the code is basically to check if the database exists in the system. If Existing Keep on using that database already built, build a new database using “Room.databaseBuilder( )”. And return the Instance.

3. Creating Repository

Why create a Repository?

As I mentioned that we are Using MVVM Architecture, We need to provide a repository class.

https://developer.android.com/jetpack/guide

What does it do?

It creates a separation between the data source and the rest of the application.

A repository mediates between data sources (such as persistent models, web services, and caches) and the rest of the app. The diagram below shows how app components such as activities that use “LiveData” might interact with data sources through a repository.

Let’s have a look at the code for the Repository.

We pass in “RegisterDatabaseDao” (dao) object as the Constructor.

We here define every Query function declared in the “RegisterDatabaseDao” which directs the execution to the Dao interface via dao Instance.

4. Designing Fragment Layouts

Let’s see how to create a new Fragment

Check out the video below to get a basic understanding of the Fragments.

File>New>Fragment>FragmentBlank.

Name them Accordingly.

Create both Fragments and Layout for all the 3 Fragments.

we have 3 Fragments

a) Register Fragment

b) Login Fragment

c) User details Fragments

a) Register Fragment Layout

This fragment contains text input fields in User data such as First Name, Last Name, UserName(Unique), Password.

It also has a button that inserts the data in the Database and navigates to the Login page if the following conditions are satisfied,

i) All the fields are filled

ii)A UserName that is not previously registered

if the following conditions don't satisfy, a Toast is shown on the screen for the same.

b)Login Fragments Layout

This fragment contains Username and Password Text input Fields.

And a Login Button that navigates to the UserDetails fragment based on the conditions given below.

i)All the fields are filled.

ii) Username exists in the database. If the data Exist check the password provided in the input is the same as the password given in the database.

Else, Show a Toast for the Same.

c)User Details Fragment Layout

This Fragment is the final destination for the app. You only reach here if the login is successful.

What does it contain?

It contains a Recycler View, which displays all the Registered Users' details on a Card view.

Note:

  1. All the fragments are Enclose in <layout> ….</layout> tag,

2. Viewmodels are declared with type inside <variables>…</variables> tag inside the <data> .. </data>tag

Below is the XML code for the Login Fragment Layout

The Rest of the fragments follow the same pattern.

5. Describing Navigation

We’ll set up the Navigation as shown above.

6. Integrating the Components with MVVM Architecture

As the Repository is ready, we’ll pass the Repository to the View models as a constructor, allowing the class to access the function inside the repository, which is directed to the dao interface.

We see the “private val repository: Repository” passed as a constructor in the above snippet. This lets the ViewModel class reach the DatabaseDoa via Repository.

The same pattern is followed on every ViewModel Class, which wants access to the database.

7. Creating Classes for Fragments, ViewModels and ViewModelFactories

Why Use ViewModel?

In a nutshell, whenever there is a configuration change in the fragment, the data is lost as the Fragment is destroyed. In order the save the data from getting lost. We store all the data in a separate file called the ViewModel, and also all the logical part of the code is handled by the ViewModel, Thus reducing the load on the UI that is the Fragment files.

UI only handles whatever is viewed on the screen, the navigation from one fragment to another when indicated by the ViewModel and other UI based tasks.

What is ViewModelFactorty?

In short, to create a constructor for the View model, We require a ViewModelFactoty From the ViewModelProviders.

ViewModel Factory code is pretty much the same for all the ViewModel, where you provide the same Constructor which you want to pass to the View Model.

Best Explained here.

let’s look at the ViewModelFactory of the Login ViewModel,

the same Code pattern follows for all the ViewModelFactory.

Let’s see the Login ViewModel Code get a better understanding.

In the Above given Code. We can see the LiveData declared under the “@Bindable” Annotation, which allows the ViewModel to Access the data directly from the Layout during the run Time.

the Functions “ login( )” and “ signUp( ) ” are used in XML via the ViewModel defined in the <data>…<data> tag

Since the ViewModel and ViewModelFactory are set up. We’ll see how to Set up the Fragment file to use their Corresponding ViewModels.

Above is the Template for setting up any Fragments, with the data stored in a Repository and View Models.

Let's look at the explanation.

Steps.

  1. Create an instance of the ViewModel as LateInit
  2. Inside OcreateView, Inflate the fragment with the corresponding Layout File.
  3. Getting a Reference of the Application Context
  4. Getting Reference to the DAO Of the database, For the current Application Context
  5. Getting Reference to the Repository by passing in the DAO Reference
  6. Getting Reference to the ViewModel Factory by Passing the Repository and application Context
  7. Getting a reference of the ViewModel Class By passing in the Factory Reference
  8. Binding a the Variable Declared in the XML with the ViewModel Reference
  9. Binding the lifecycle Owner the current Class

10.The Last step is to return binding.root

These are the steps involving in setting up the Fragment, Right before returning the binding.root we provide the code for Observing the LiveData given in the ViewModel.

The steps given above is mostly boilerplate, you can reuse it when ever we are creating a fragment that uses ViewModel, ViewModelFactoty, and Repository or Dao itself

the same pattern goes to the Register Fragment as well as the UserDetails Fragment

10. Coding the Logic

Alright! With everything setup. We’ll see What's the Logic behind the Login Process.

A) We’ll begin with the Registration Fragment (Considering Fragment, ViewModel and ViewModel is already set up)

As we discussed the conditions in the 4th Step, we want to check whether.

i) All the fields are filled

ii)A UserName that is not previously registered

the Code goes into the RegisterButton Function

Step 1:

As the String from the Text Fields is assigned to the LiveData Variables InputFirstName, InputLastName, InputUserName, InputPassword.

The First If Condition Checks if any of the Fields are Null. If true, then Set the LiveData errorToast.Valur= true .

What happens if we make the errorToast.Valur= true, We have defined, and Observer in the Fragment class, wich Observes the LiveData the code goes like

If the changed value is True, show the Toast.

Step 2:

If all the Input Fields are filled, It jumps to the Else case, and Here we start a new Coroutine Block [uiScope.launch]

what’s uiscope?

Whenever we have to perform a DataBase Operation, we have to run it using a coroutine. Here we get a reference of the coroutine as uiscope.

To have a better understanding of Coroutines, check out the link below

val usersNames = repository.getUserName(inputUsername.value!!) 

the above code returns the Query function defined inside ViewModel Class.

Which is directed to getInsert( ) function defined in Repository to the getInsert( ) Function in the DAO

It takes the String Username as an Argument and Returns the Records of the row if the Username Exists in the database as an Object of type “RegisterEntity.” Else returns Null.

Step 3:

if (usersNames != null) {
_errorToastUsername.value = true
} else {
val firstName = inputFirstName.value!!
val lastName = inputLastName.value!!
val email = inputUsername.value!!
val password = inputPassword.value!!
insert(RegisterEntity(0, firstName, lastName, email, password))
inputFirstName.value = null
inputLastName.value = null
inputUsername.value = null
inputPassword.value = null
_navigateto.value = true
}

if userName Return data means the Username is already existing in the database and Has to show a Toast on the fragment saying Username Exist! Just as the Toast described in Step1.

Step 4:

In the else case, the Flow of the Code reaches here only If all the conditions are satisfied.

Here we create a set of Local variables and assign the Text Input to them and pass them to the Insert Function defined in the ViewModel Class.

the insert function works the same way as the “getUsers( )” function Described in Step 2

Insert Function, Takes a “RegiserEntity” object as an argument and inserts all the data into the DataBase.

Set all the input Fields to Null and Navigate to the LoginFragment!.

B) Let's Look at the Login Fragment :

Login Fragment is similar to Register Fragment, and Here we Have only two Text Input Fields, Username and Password. As discussed in Step 4-(Designing Fragments )

i)All the fields are filled.

ii) Username exists in the database. If the data Exist check the password provided in the input is the same as the password given in the database.

Code for the fun loginButton( )

Step 1:

As the String from the Text Fields is assigned to the LiveData Variables InputUserName, InputPassword.

The First If Condition Checks if any of the Fields are Null. If true, then Set the LiveData errorToast.Valur= true .

What happens if we make the errorToast.Valur= true, We have defined, and Observer in the Fragment class, wich Observes the LiveData the code goes like

If the changed value is True, show the Toast.

Step 2:

If all the Input Fields are filled, It jumps to the Else case, and Here we start a new Coroutine Block [uiScope.launch]

val usersNames = repository.getUserName(inputUsername.value!!)

The above code returns the Query function defined inside ViewModel Class.

Which is directed to getInsert( ) function defined in Repository to the getInsert( ) Function in the DAO

It takes the String Username as an Argument and Returns the Records of the row if the Username Exists in the database, as an Object of type “RegisterEntity.”
Else returns Null.

Step 3:

Now, if the getUsers Returns a RegisterEntity Object, That means The Username is valid.
Now, as we know, it’s a Valid Username. We check if the provided password matches with the Password in the database.

If the password doesn't match
we Show a toast saying, “ Please check your Password.”

Just like the Toast in Step 1

Step 4:

If the getUsers Returns NULL,
this means the Current Username Doesn't Exist in the Database, Hence Show a Toast Sayin “User doesn't exist, please Register!”

Step 5:

If the Username and password exist in the database.
This reaches the final destination for the function, where the UI is navigated to the UserDetails Fragment.

C) User Details Fragment:

Let's see the UserViewModel Code.

In the above code,

val users = repository.users

here the “users” contains a reference to the Live data, Which contains all the records of the table as a LiveData as a List.

This Live Data is accessed in the UserFragment using Observer and is Passed to the RecycletView.

I have Summarized the Vital Parts of the Project.

To Have a Better Understanding, Download the source code from the Github Repository Mentioned below.

I hope this Helps. Thank you!

--

--

Sulu Mufi

Masters In Computer Applications, Pondicherry Central University.