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
相关产品推荐
相关产品推荐

