Best practice to implement MVVM in fragment using Bottom navigation android kotlin
Asked Answered
M

1

2

I am implementing the MVVM in fragments using the bottom navigation with firebase. But it's not working in my case. I searched for many solutions but didn't solve my problem.

I implemented viewModel in the fragment and apply observer to it. In the ViewModel class, call the method(getting data from firebase) with return type LiveData from the repository.

I'm using these dependencies

dependencies

//    Fragment Navigation using JetPack
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'

Repository.kt


    // Get emergency requests
    fun getEmergencyRequests(): LiveData<MutableList<EmergencyRequests>> {
        val mutableData = MutableLiveData<MutableList<EmergencyRequests>>()

        val requestListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                if (dataSnapshot.exists() && dataSnapshot.hasChildren()) {

                    // Get Post object and use the values to update the UI
                    val listData = mutableListOf<EmergencyRequests>()

                    for (snapshot in dataSnapshot.children) {
                        val model = snapshot.getValue(EmergencyRequests::class.java)

                        model?.let {
                            model.requestId = snapshot.ref.key.toString()
                            listData.add(it)
                        }
                    }

                    listData.sortByDescending { it.timestamp }
                    mutableData.value = listData


                }
            }

            override fun onCancelled(error: DatabaseError) {
                Log.e("EMERGENCIES_FAIL", error.message)
            }

        }

        emergencyRequestRef?.addValueEventListener(requestListener)

        return mutableData
    }

ViewModel.kt

private val repository: HomeRepository = HomeRepository()

    fun getRequests(): LiveData<MutableList<EmergencyRequests>> {
        repository.getEmergencyRequests()
        val mutableData = MutableLiveData<MutableList<EmergencyRequests>>()
        repository.getEmergencyRequests().observeForever {
            mutableData.value = it
        }
        return mutableData
    }

Fragment.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

//        Initialize
        initialization()

//        Recyclerview
        binding.recyclerView.layoutManager = LinearLayoutManager(mContext)
        adapter = EmergenciesAdapter(mContext,  object : OnRequestClick {
            override fun onClick(model: EmergencyRequests) {

            }
        })
        binding.recyclerView.adapter = adapter

//        Get emergencies
//        getEmergencies()
        viewModel = ViewModelProvider(mContext).get(HomeViewModel::class.java)


        observer()

    }

    private fun observer() {
        viewModel.getRequests().observe(viewLifecycleOwner, {
            adapter.setDataList(it)
            adapter.notifyDataSetChanged()
        })
    }
Mhd answered 15/7, 2021 at 14:4 Comment(4)
Hi there, do you want to solve the problem or best practice for bottom navigation ?Coffee
If it is solved by best practice, It will be best for meMhd
Since you said solved, so what is the problem ?Coffee
Fragment always recreate, I want only 1st-time fragment to create whenever I go to the visited fragment it shouldn't recreate, it must save their stateMhd
Q
2

Not sure if this will fix it for you, but if you are using bottom navigation view and want fragments to maintain a backstack for the visited destinations, then with this version of Navigation dependency you can get to save the backstacks automatically, use these Navigation dependencies:

implementation 'androidx.navigation:navigation-fragment-ktx:2.4.0-alpha01'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.0-alpha01'

UPDATE

In your repository, are you able to get the data back? since the function might be returning before it gets data from firebase. I would suggest refactoring it a bit like this:

Repository.kt

private val _data: MutableLiveData<MutableList<EmergencyRequests>> = MutableLiveData()

val data: LiveData<MutableList<EmergencyRequests>> get() = _data

// Get emergency requests
fun getEmergencyRequests() {

    val requestListener = object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
            if (dataSnapshot.exists() && dataSnapshot.hasChildren()) {

                // Get Post object and use the values to update the UI
                val listData = mutableListOf<EmergencyRequests>()

                for (snapshot in dataSnapshot.children) {
                    val model = snapshot.getValue(EmergencyRequests::class.java)

                    model?.let {
                        model.requestId = snapshot.ref.key.toString()
                        listData.add(it)
                    }
                }

                listData.sortByDescending { it.timestamp }
                _data.value = listData


            }
        }

        override fun onCancelled(error: DatabaseError) {
            Log.e("EMERGENCIES_FAIL", error.message)
        }

    }

    emergencyRequestRef?.addValueEventListener(requestListener)
}

ViewModel.kt

private val repository: HomeRepository = HomeRepository()

fun getRequests(): LiveData<MutableList<EmergencyRequests>> {
    repository.getEmergencyRequests()
    
    return repository.data
}
Quicksand answered 15/7, 2021 at 14:16 Comment(6)
No need to change fragment and viewModel class?Mhd
fragment should still get the livedata from viewmodel, i'll update my answer and add code for viewmodel as well :)Quicksand
Okay, I'm waiting for your answer. thanks in advanceMhd
Thanks for your answer. It's working now. You saved me a lot of time.Mhd
In case it fixes everything for you and there aren't any more queries from your side, kindly mark it as accepted answer for your question :)Quicksand
I can't mark until my reputation minimum of 15. If you upvote my question maybe my reputation increases to 15 then I'llable to mark as accepted. Please upvote my questionMhd

© 2022 - 2024 — McMap. All rights reserved.