Search code examples
androidkotlinmvvmretrofitdagger

How to share data between two fragments? Having trouble with the MVVM architecture


Currently I have two fragments, one that shows the weather for a place and another that let's you select a point in a map and redirects you to the other fragment showing the weather for that point.

What I want to achieve is to have by default the user's location and give the option to the user by going to the map of selecting any other place. So I would like to:

  • Get lat/lng via GPS and then "upload" it to somewhere that would share those values between this fragment and the map fragment (The activity maybe?)
  • If a point in the map is selected update those values- Both fragments can read at any time, only map fragment can update

I believe I can do this sketchily adding getters/setters to my activity and then in the fragments cast the activity to my particular activity. But this just seems bad. What would be the correct way to do it? What I have now:

  • MainActivity (connects both fragments through a bottom navigation bar)
  • ForecastFragment (Shows the weather for lat/lng)
  • ForecastViewModel
  • MapFragment
  • MapViewModel
  • WeatherRepository
  • WeatherAPI (through RetroFit updates values)

I'm using dagger and kotlin.

Thanks!


Solution

  • Share a common view model between ForecastFragment and MapFragment using the activity scope

    Take a look at the example provided here Share data between fragments

    class SharedViewModel : ViewModel() {
        val selected = MutableLiveData<Item>()
    
        fun select(item: Item) {
            selected.value = item
        }
    }
    
    class MasterFragment : Fragment() {
    
        private lateinit var itemSelector: Selector
    
        // Use the 'by activityViewModels()' Kotlin property delegate
        // from the fragment-ktx artifact
        private val model: SharedViewModel by activityViewModels()
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            itemSelector.setOnClickListener { item ->
                // Update the UI
            }
        }
    }
    
    class DetailFragment : Fragment() {
    
        // Use the 'by activityViewModels()' Kotlin property delegate
        // from the fragment-ktx artifact
        private val model: SharedViewModel by activityViewModels()
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
                // Update the UI
            })
        }
    }
    

    Note:

    Both fragments must handle the scenario where the other fragment is not yet created or visible.