Search code examples
androidkotlinmvvmviewmodelbottomnavigationview

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


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()
        })
    }

Solution

  • 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
    }