6

I just started learning jetpack compose. I have a very basic question. My ViewModel has a SingleLiveEvent that I use to navigate to another screen.

private val _navigateToDetails: SingleLiveEvent<Movie> = SingleLiveEvent()
val navigateToDetails: MutableLiveData<Movie> = _navigateToDetails

I know that I can use Livedata as state to emit UI but how to use it to trigger some action within composable.

Previously I had used viewLifecycleOwner to observer the state as anyone would do like this.

viewModel.navigateToDetails.observe(viewLifecycleOwner) {
    // navigate to details
}

How do I achieve the same thing in compose. I don't know if that's possible or not. Maybe I am not thinking this in compose way. Any help would be appreciated.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Susan Thapa
  • 501
  • 5
  • 9

2 Answers2

3

I would do something like to make sure I'm only doing it once:

@Composable
fun LoginScreen(viewModel: LoginViewModel) {
    val loginState by viewModel.loginState.observeAsState(null)
    val hasHandledNavigation = remember { mutableStateOf(false)}
    if (loginState != null && !hasHandledNavigation.value ) {
        navigateToWelcomeScreen()
    else {
        // rest of the Compose UI
    }
}

UPDATE: Option two, you can also just pass the action of going to next screen to viewmodel and fire it up there.

Amin Keshavarzian
  • 3,646
  • 1
  • 37
  • 38
1

Actually, in compose we use mutableStateOf() over LiveData. In your viewmodel, you can change the type of the data holder from LiveData to mutableStateOf(...) which will allow you to directly use it in your Composables without explicitly calling observe()

Let's say you wish to store an integer of any kind in your viewmodel and update the state of your Composable based on that.

In your viewmodel,

var mInteger by mutableStateOf (0) //'by' helps treat state as data

fun updateMInteger(newValue: Int){
mInteger = newValue
}

In your Composable, directly call viewmodel.mInteger and Compose being built like that, automatically updates the UI, given that mInteger is being read from the Composable

Like

Text(viewModel.mInteger)
Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
  • 2
    I think you didn't get the question. I am not trying to emit a UI instead I am trying to trigger some code (may be a lambda) when the livedata changes within composable. I tried to trigger some code and it worked but the problem is it doesn't behave like SingleLiveData and triggers the event every time I return back to the previous screen. – Susan Thapa Jun 12 '21 at 00:32
  • Are you using some non-primitive data type as state? I am almost certain that what you describe is happening because the state is being stored in the state holder, but the code (Seemingly a Composable) that you want to trigger with the change is not treating it as state. You see if you want to trigger some code in a Composable on the change of a value in any other part of the program, you need to ensure that you either use a primitive type state, like mutableStateOf (Int), or define a custom MutableState because you cannot just write mutableStateOf (T) to use something as state – Richard Onslow Roper Jun 13 '21 at 19:27
  • I mean that'd be my experience – Richard Onslow Roper Jun 13 '21 at 19:27