Kotlin with MVVM application PART 4(Last Part)

Süleyman Başaranoğlu
4 min readDec 6, 2021

Hi everyone we are now at the last chapter. We create an all about Food section. First, we need an API, I used this API(click it).

Get a JSON file and use the Kotlin data class file from JSON on Android Studio and paste JSON data like this and the model class will be created automatically.

JSON GENERATE

DATA(MODEL&NETWORK)

Model(It Created if you use JSON generate automatically)

FOLDER MODEL

Let’s create Retrofit for Networking create network folder and Api + Service files.

FOLDER NETWORK

API Interface

interface FoodApi {
@GET("food-database/v2/parser?app_id=a00**58e&app_key=e7d27a4853f***44181583be6dd1f7e2") <- My KEY and ID for API
fun getFoods(@Query("ingr") searchText: String) <- For search term Single<FoodApiModel>
}

point: Single on RxJava: A Single is something like an Observable, but instead of emitting a series of values — anywhere from none at all to an infinite number — it always either emits one value or an error notification.

Service

private val BASE_URL = "https://api.edamam.com/api/"  
private val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(FoodApi::class.java)

fun getData(searchText:String): Single<FoodApiModel> {
return api.getFoods(searchText) <- Get for Search Term
}

UI (ADAPTER&VİEWMODEL&FRAGMENT)

We need Recycler View, ProgressBar for when I refresh the page, TextView for networking Errors, and EditText for Food search.

UI

Here it is my food_item, one of the items in my Recycler View.

RECYCLER VIEW ITEMS

Adapter

We created one adapter on PART 3. Let’s create again.

...... Create like in PART 3 Adapter(click for detail)...fun updateFoodWhenRefresh(newFoodList: List<FoodApiModel>) {
foodsList.clear() <- When refresh I clear my list
foodsList.addAll(newFoodList) <- After add new food
}
onBindViewHolder : holder.binding.tvFoodLabel.text=foodsList[position].hints.firstOrNull()?.food?.label.toString()
...
... <- Other Items
...
Picasso.get().load(foodsList[position].hints.firstOrNull()?.food?.image).into(holder.binding.ivFood) <- For Image I used Picasso Library

I want to go Food Details When I click one Item on Recycler View Let’s create this function in onBindViewHolder

holder.itemView.setOnClickListener {
val intent = Intent(holder.itemView.context,FoodDetails::class.java) <- Which page I want to go
intent.apply {
putExtra("Label", currentItem.hints.firstOrNull()?.food?.label) <- Remember KEY / VALUE in => PART 2
...
...
...
holder.itemView.context.startActivity(intent)
}
FOOD DETAILS

VİEWMODEL

private val foodApiService = FoodApiService() <-Call my service 
private val disposable = CompositeDisposable() **description is below
Remember my UI -> (RecyclerView get list data, error(TextView) and loading(ProgressBar) cases
val foodsData = MutableLiveData<List<FoodApiModel>>()
val foodsError = MutableLiveData<String>()
val foodsLoading = MutableLiveData<Boolean>()

point: Disposable is a stream or a link between an Observable and an Observer. A quick check of the documentation. Click for details

I need to get data from API now let’s create this function.

private fun getDataFromAPI(searchText: String) {    foodsLoading.value = true    disposable.add(
foodApiService.getData(searchText) <- I called My function in my Service
Click for details for 1/2/3 1 .subscribeOn(Schedulers.newThread())
2 .observeOn(AndroidSchedulers.mainThread())
3 .subscribeWith(object : DisposableSingleObserver<FoodApiModel>() {

override fun onSuccess(t: FoodApiModel) {
foodsData.value = listOf(t)
foodsError.value = ""
foodsLoading.value = false
}

override fun onError(e: Throwable) {
foodsError.value = e.message
foodsLoading.value = false
}
})
)
}

and

fun getData(searchText: String) {
getDataFromAPI(searchText)
}

Fragment

Global variables

lateinit var foodAdapter: FoodAdapter <- Call my adapter 
private val _viewModel by inject<FoodViewModel>() <- KOIN
private lateinit var binding: FragmentRecipesBinding <-View Binding
First time I use Shared Preferences in this Project
private lateinit var getPreferences: SharedPreferences
private lateinit var setPreferences: SharedPreferences.Editor

Initialize Shared Preferences

getPreferences = requireActivity().getSharedPreferences("pref", MODE_PRIVATE) <- Get Shared Pref initializesetPreferences = getPreferences.edit()<- Set Shared Pref initialize

I need to observe my data.

private fun observeLiveData() {
If I got Error
_viewModel.foodsError.observe(viewLifecycleOwner, Observer { errorMessage ->
...Create for yourself

}
If I got Refresh
_viewModel.foodsLoading.observe(viewLifecycleOwner, Observer { loading ->
... Create for yourself
If I got live data
_viewModel.foodsData.observe(viewLifecycleOwner, Observer { foods ->
foods?.let {
binding.recyclerView.visibility = View.VISIBLE
foodAdapter.updateFoodWhenRefresh(foods) <- In my adapter function
}
}
)

Now when I search term and click search I need a function

fun searchFood(){
... <- Binding UI
val foodName = binding.edSearchFood.text.toString()
setPreferences.putString("foodName", foodName)
setPreferences.apply()
_viewModel.getData(foodName)
observeLiveData()
}

onCreateView

xval foodName = getPreferences.getString("foodName", "")?.lowercase(Locale.getDefault()) <- Use preferences and default value EMPTY STRING binding.edSearchFood.setText(foodName) <- Search Term is foodName value _viewModel.getData(foodName) <- Use getDataFromAPI function on viewModel observeLiveData() <- Above function
searchFood() <- Above function
note : The rest is the same as the previous parts

**extra point

If I want to add food and show it on the daily menu

If click to add food you use this way var totalCalories = 0
var addCount = 0
if click add
addCount++
or click remove
addCount--
click to add button, get total calories maybe like this
addCount * get calorie food in model + total calorie
click to daily food menu
use putExtra

Let’s watch the whole application

This article is 4 and last part of my MVVM app :)

Github link is: https://github.com/basaransuleyman/MustfitMVVMJetpack Linked is: https://www.linkedin.com/in/basaransuleyman/

--

--