Open In App

How to Build a Food and Their Price List Android App using MVVM Architecture with Kotlin?

Last Updated : 08 Apr, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we will food app that displays a list of foods and their price using the retrofit library and MVVM architecture. Model — View — ViewModel (MVVM) is the industry-recognized software architecture pattern that overcomes all drawbacks of MVP and MVC design patterns. MVVM suggests separating the data presentation logic(Views or UI) from the core business logic part of the application. A sample video is given below to get an idea about what we are going to do in this article.

The separate code layers of MVVM are:

  • Model: This layer is responsible for the abstraction of the data sources. Model and ViewModel work together to get and save the data.
  • View: The purpose of this layer is to inform the ViewModel about the user’s action. This layer observes the ViewModel and does not contain any kind of application logic.
  • ViewModel: It exposes those data streams which are relevant to the View. Moreover, it serves as a link between the Model and the View.

 

Step-by-Step Implementation

Step 1: Create a New Project in Android Studio

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.

Step 2: Add Required Dependencies

Add Retrofit dependencies in Build.gradle(app) file

Kotlin




// retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'


Add View Model and Live Data dependencies in Build.gradle(app) file

Kotlin




def lifecycle_version = "2.6.0-alpha01"
// ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
// ViewModel utilities for Compose
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
// LiveData
implementation("androidx.lifecycle:lifecycle-live data-ktx:$lifecycle_version")


Add Glide Dependency in Build.gradle(app) file

Kotlin




implementation 'com.github.bumptech.glide:glide:4.13.2'


Glide library helps in image processing in android.

Step 3: Enable View Binding

To enable view binding add this code inside the android {} block in build.gradle(app) file

Kotlin




buildFeatures {
    viewBinding = true
}


Step 4: Generate data classes

Right-click on the root package and select New > Kotlin-data class from JSON. If you don’t have this plugin, go to File -> Settings -> Plugin and install JSON to Kotlin Plugin. Copy the JSON result and paste it. Give this file a suitable name after that data classes will be generated.

Kotlin




data class Data(
    val CatId: String,
    val CatName: String,
    val LongDescription: String,
    val MasterCatId: String,
    val MasterCatName: String,
    val PrdBrandId: String,
    val PrdBrandName: String,
    val PrdId: String,
    val PrdImageUrl: String,
    val PrdName: String,
    val PrdPrice: String,
    val PrdPriceShow: String,
    val PrdRating: String,
    val PrdratingCount: String,
    val ProductImages: List<ProductImage>,
    val ProductTypeId: String,
    val ProductTypeImageurl: String,
    val ProductTypeName: String,
    val PtrPrdvariantList: List<PtrPrdvariant>,
    val ShortDescription: String,
    val SubCatId: String,
    val SubCatName: String
)


Kotlin




data class MealList(
    val Data: List<Data>
)


Right Click on the Root package and create an interface APi

Kotlin




import com.room.daisoftware.pojo.MealList
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
  
interface Api {
  
    @GET("Fetch_ProductList_By_PartnerId_SubCatId?")
    fun getData(@Query("SubCatId") Sid:String , @Query("PartnerId") Pid:String ):Call<MealList>
  
}


Create a Retrofit Instance

Kotlin




import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
  
object RetrofitInstance {
  
    val api:Api by lazy {
        Retrofit.Builder()
            .baseUrl("https://www.deshizon.com/api/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(Api::class.java)
    }
}


Step 5: Design layout Files

Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Below is the code for the activity_main.xml file. Comments are added inside the code to understand the code in more detail.

XML




<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#EAEAEA"
    android:orientation="vertical"
    tools:context=".activities.MainActivity">
  
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rcv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:layout_weight="1.7"
        tools:listitem="@layout/items" />
  
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.3"
        android:orientation="horizontal">
        
  </LinearLayout>
  
</LinearLayout>


Since we are using recycler view we need to create a new layout file for that go to res > layout and create a new Layout resource file named items.xml

XML




<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
  
    <androidx.cardview.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:elevation="10dp"
        app:cardCornerRadius="15dp">
  
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
  
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="80dp"
                android:layout_height="80dp"
                android:layout_margin="10dp"
                tools:srcCompat="@tools:sample/avatars" />
  
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_marginStart="30dp"
                android:orientation="vertical">
  
                <TextView
                    android:id="@+id/textView"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:text="TextView"
                    android:textColor="@color/black"
                    android:textSize="13sp"
                    android:textStyle="bold" />
  
                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">
  
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="8dp"
                        android:text="price - "
                        android:textColor="#D13838"
                        android:textSize="13sp"
                        android:textStyle="bold" />
  
                    <TextView
                        android:id="@+id/price"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="8dp"
                        android:text="TextView"
                        android:textColor="#D13838"
                        android:textSize="13sp"
                        android:textStyle="bold" />
                </LinearLayout>
  
            </LinearLayout>
  
        </LinearLayout>
  
    </androidx.cardview.widget.CardView>
  
</LinearLayout>


Step 6: Create an ApiAdapter  class for RecyclerView

Kotlin




import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.room.daisoftware.databinding.ItemsBinding
import com.room.daisoftware.pojo.Data
import com.room.daisoftware.pojo.MealList
  
class ApiAdapter(): RecyclerView.Adapter<ApiAdapter.ApiViewHolder>() {
  
    private var foodList = ArrayList<Data>()
  
    fun setMealList(charList : List<Data>){
        this.foodList = charList as ArrayList<Data>
        notifyDataSetChanged()
    }
  
  
    class ApiViewHolder(val binding: ItemsBinding):RecyclerView.ViewHolder(binding.root)
  
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ApiViewHolder {
        return ApiViewHolder(ItemsBinding.inflate(LayoutInflater.from(parent.context),parent,false))
    }
  
    override fun onBindViewHolder(holder: ApiViewHolder, position: Int) {
        Glide.with(holder.itemView)
            .load(foodList[position].PrdImageUrl)
            .into(holder.binding.imageView)
        holder.binding.textView.text = foodList[position].PrdName
        holder.binding.price.text = foodList[position].PrdPrice
    }
  
    override fun getItemCount(): Int {
        return foodList.size
    }
}


Step 7: Create Meal View Model

Since we are using MVVM architecture we need to create a View Model Class with live data in it.

Kotlin




import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.room.daisoftware.pojo.Data
import com.room.daisoftware.pojo.MealList
import com.room.daisoftware.retrofit.RetrofitInstance
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
  
class MealViewModel : ViewModel() {
  
    private var MealLiveData = MutableLiveData<List<Data>>()
    fun getMeals() {
        RetrofitInstance.api.getData("507","180").enqueue(object : Callback<MealList> {
            override fun onResponse(call: Call<MealList>, response: Response<MealList>) {
                if (response.body() != null) {
                    MealLiveData.value = response.body()!!.Data
                } else {
                    return
                }
            }
  
            override fun onFailure(call: Call<MealList>, t: Throwable) {
                Log.d("TAG", t.message.toString())
            }
  
        })
    }
  
    fun ObserveMealLiveData(): LiveData<List<Data>> {
        return MealLiveData
    }
}


Step 8: Write Code for UI

In the end, we will write code for the main activity and meal activity which represents our UI

MainActivity.kt file:

Kotlin




import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.room.daisoftware.adapter.ApiAdapter
import com.room.daisoftware.databinding.ActivityMainBinding
import com.room.daisoftware.viewModel.MealViewModel
  
class MainActivity : AppCompatActivity() {
  
    private lateinit var binding: ActivityMainBinding
    private lateinit var ApiAdapter:ApiAdapter
    private lateinit var mealViewModel:MealViewModel
  
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        prepareRecyclerView()
        mealViewModel = ViewModelProvider(this)[MealViewModel::class.java]
        mealViewModel.getMeals()
        mealViewModel.ObserveMealLiveData().observe(this, Observer {
                ApiAdapter.setMealList(it)
        })
  
    }
  
    private fun prepareRecyclerView() {
        ApiAdapter = ApiAdapter()
        binding.rcv.apply {
            layoutManager = LinearLayoutManager(applicationContext)
            adapter = ApiAdapter
        }
    }
  
}


Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads