androidkotlinandroid-jetpack-composeandroid-viewmodelstart-activity

Best practice to implement startActivity using ViewModel in Jetpack Compose


For example, I have this simple Composable function

@Composable
fun TextExample(model: SomeViewModel = viewModel()) {
    TextButton(onClick = { model.onClick() }) {
        Text(text = "Test")
    }
}

The SomeViewModel:

class SomeViewModel : ViewModel() {
    private val _text = mutableStateOf("Test")
    val text: String
        get() = _text.value

    fun onClick() {
        if (text.isEmpty()) {
            // TODO: need to start some activity
        } else {
            _text.value = ""
        }
    }
}

I clicking this Button and then the model must to handle this click. In some cases I need to start another activity. What is right way to do this?


Solution

  • There might better approach to this but you can consider mine.

    Id suggest first to create a data structure for "one time events" with a Sealed Class like this

    sealed class Events {
        object ToActivityA: Events()
        object ToActivityB: Events()
        data class ToastMessage(val message: String): Events()
    }
    

    Declare a SharedFlow in your ViewModel that will emit these events

    private val _events = MutableSharedFlow<Events>()
    val events: SharedFlow<Events> = _events
    

    and in your case, emit an event from your onClick viewmodel function like this

    fun onClick() {
        if (text.isEmpty()) {
            viewModelScope.launch {
                _events.emit(Events.ToActivityA) // or ToActivityB, or for a Toast 
            }
        } else {
            _text.value = ""
        }
    }
    

    Now in your composable, just observe it in a LaunchedEffect like this

    LaunchedEffect(key1 = Unit) {
            viewModel.events.collectLatest {
                when (it) {
                    Events.ToActivityA -> {
                        // to activity A
                    }
                    Events.ToActivityB -> {
                        // to activity B
                    }
                    is Events.ToastMessage -> {
                        // show toast message
                    }
                }
            }
        }
    

    If you don't have something prepared yet for calling startActivity, I'd suggest visiting this post as a reference.