Home > Back-end >  How to navigate on view model field change in Compose Navigation?
How to navigate on view model field change in Compose Navigation?

Time:11-26

In my app I want to send info to server and after receiving successful response I want to pass info to current screen to navigate to another screen.

Here's the flow:

From UI I call viewModel to send request to server. In ViewModel I have a callback:

@HiltViewModel
class CreateAccountViewModel @Inject constructor(
    private val cs: CS
) : ViewModel() {

    private val _screen = mutableStateOf("")

    val screen: State<String> = _screen

    fun setScreen(screen: Screen) {
        _screen.value = screen.route
    }

    private val signUpCallback = object : SignUpHandler {
        override fun onSuccess(user: User?, signUpResult: SignUpResult?) {
            setScreen(Screen.VerifyAccountScreen)
            Log.i(Constants.TAG, "sign up success")

        }

        override fun onFailure(exception: Exception?) {
            Log.i(Constants.TAG, "sign up failure ")
        }

    }
}

As you can see I have also State responsible for Screen so when response is successful I want to update the state so UI layer (Screen) knows that it should navigate to another screen. My question is: how can I observer State in

@Composable
fun CreateAccountScreen(
    navController: NavController,
    viewModel: CreateAccountViewModel = hiltViewModel()
) {
}

Or is there a better way to achieve that?

CodePudding user response:

I think your view model should know nothing about navigation routes. Simple verificationNeeded flag will be enough in this case:

var verificationNeeded by mutableStateOf(false)
    private set

private val signUpCallback = object : SignUpHandler {
    override fun onSuccess(user: User?, signUpResult: SignUpResult?) {
        verificationNeeded = true
        Log.i(Constants.TAG, "sign up success")
    }

    override fun onFailure(exception: Exception?) {
        Log.i(Constants.TAG, "sign up failure ")
    }
}

The best practice is not sharing navController outside of the view managing the NavHost, and only pass even handlers. It may be useful when you need to test or preview your screen.

Here's how you can navigate when this flag is changed:

@Composable
fun CreateAccountScreen(
    onRequestVerification: () -> Unit,
    viewModel: CreateAccountViewModel = hiltViewModel(),
) {
    if (viewModel.verificationNeeded) {
        LaunchedEffect(Unit) {
            onRequestVerification()
        }
    }
}

in your navigation managing view:

val navController = rememberNavController()
NavHost(
    navController = navController,
    startDestination = Screen.CreateAccount
) {
    composable(Screen.CreateAccount) {
        CreateAccountScreen(
            onRequestVerification = {
                navController.navigate(Screen.VerifyAccountScreen)
            }
        )
    }
}
  • Related