androidkotlinandroid-viewbinding

How to navigate to another activity based on viewmodel's logic, with view binding / MVVM?


I have:

I have a button in fragment_main.xml. I can easily pass it android:onClick="navigate" and have the function navigate() in MainActivity create an intent and start SecondActivity like so:

    fun navigate(v: View) {
        intent = Intent(this, SecondActivity::class.java)
        startActivity(intent)
    }

But what if I want the button to navigate to a different activity (ThirdActivity) if a bool is true?

This is what I've tried, but the binding in the MainActivity doesn't work:

class MainViewModel : ViewModel() {
    val navBoolLiveData = MutableLiveData<Boolean>()

    fun navigate() {
        val boolio = false
        navBoolLiveData.postValue(boolio)
    }
}
class MainActivity : AppCompatActivity(R.layout.activity_main) {

    private val viewModel: MainViewModel by viewModels()

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        if (savedInstanceState == null) {
            supportFragmentManager.commit {
                setReorderingAllowed(true)
                add<MainFragment>(R.id.fragment_container_view)
            }
        }
    }

    private fun navigateObserver() {
// this is never called
        viewModel.navBoolLiveData.observe(this) { boolio ->
            if (!boolio) {
                intent = Intent(this, SecondActivity::class.java)
                startActivity(intent)
            } else {
                intent = Intent(this, ThirdActivity::class.java)
                startActivity(intent)
            }
        }
    }
}

I have a feeling I'm going about this all wrong. My binding in MainFragment does work, but I wasn't sure it should even be involved in the navigation logic or actual nav call.

What is the right way to do this?


Solution

  • You are missing call to navigateObserver() in your onCreate(). Also, to use LiveData for events such as navigation, you should reset the state after the change is consumed. i.e. after you are done navigating. This is help in preventing bugs. Below is an example:

    1. Create a LiveData for navigation event in your ViewModel
      val navigationEvent = MutableLiveData<Boolean>()
      
    2. Create a fun to reset the state of event, after you navigated
      fun navigationEventComplete() {
           navigationEvent.value = false
      }
      
    3. Observe the event in your fragment
      viewModel.navigationEvent.observe(viewLifecycleOwner, Observer {
               if (it == true) {
                   navigate()
                   // after navigation reset the state, to signal you are done navigating
                   viewModel.navigationEventComplete()
               }
           })