You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose登录页面状态变更后多次重组 导致导航重复触发问题

问题根因

你将导航跳转逻辑直接写在了Composable的重组作用域中,只要loginState.value为true,每次LoginScreen发生重组都会触发一次导航操作,自然就会出现重复跳转的问题。同时ViewModel中的loginState被设为true后没有手动重置,就算页面被切走再切回、或者任何触发重组的场景都会再次触发跳转。

修复方案

1. 用LaunchedEffect包裹导航逻辑

导航属于副作用操作,必须放到Compose的副作用函数中执行。LaunchedEffect会仅在指定的key发生变化时执行一次,不会随重组反复触发,修改LoginScreen中的跳转逻辑如下:

@Composable
fun LoginScreen(
    navController: NavController,
    viewModel: LoginViewModel = hiltViewModel()
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.SpaceEvenly
    ) {
        val email by viewModel.email
        val password by viewModel.password
        val enabled by viewModel.enabled

        // 替换原有直接判断跳转的代码
        LaunchedEffect(viewModel.loginState.value) {
            if (viewModel.loginState.value) {
                navController.navigate(Screen.HomeScreen.route) {
                    popUpTo(Screen.LoginScreen.route) {
                        inclusive = true
                    }
                }
                // 跳转完成后重置登录状态,避免状态残留
                viewModel.resetLoginState()
            }
        }

        LoginHeader()
        LoginForm(
            email = email,
            password = password,
            onEmailChange = { viewModel.onEmailChange(it) },
            onPasswordChange = { viewModel.onPasswordChange(it) }
        )
        LoginFooter(
            enabled,
            onLoginClick = {
                viewModel.login()
            },
            onRegisterClick = {
                navController.navigate(Screen.RegisterScreen.route)
            }
        )
    }
}

2. 在ViewModel中添加状态重置方法

避免登录状态残留导致后续误触发:

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val loginRepository: LoginRepository,
) : BaseViewModel() {

    val email = mutableStateOf(EMPTY)
    val password = mutableStateOf(EMPTY)
    val enabled = mutableStateOf(false)
    val loginState = mutableStateOf(false)

    fun onEmailChange(email: String) {
        this.email.value = email
        checkIfInputsValid()
    }

    fun onPasswordChange(password: String) {
        this.password.value = password
        checkIfInputsValid()
    }

    private fun checkIfInputsValid() {
        enabled.value =
            Validator.isEmailValid(email.value) && Validator.isPasswordValid(password.value)
    }

    fun login() = viewModelScope.launch {
        val response = loginRepository.login(LoginRequest(email.value, password.value))
        loginRepository.saveSession(response)
        loginState.value = response.success ?: false
    }
    
    // 新增状态重置方法
    fun resetLoginState() {
        loginState.value = false
    }
}

可选优化方案

你也可以将loginState改为一次性事件结构,比如用Channel或者SharedFlow来发送登录成功事件,订阅时自动消费,不需要手动重置状态,也能从根源避免重复触发的问题。


内容的提问来源于stack exchange,提问作者Berkay Kireçci

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 13:06:03