Ber*_*çci 4 android kotlin android-jetpack-compose
我正在从事撰写项目。我有简单的登录页面。单击登录按钮后,登录状态在视图模型中设置。问题是当我在服务调用后设置 loginState 时,我的可组合项会多次重新组合。因此,navcontroller 会进行多次导航。我不明白这个问题。谢谢你的帮助。
我的可组合项:
@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
if (viewModel.loginState.value) {
navController.navigate(Screen.HomeScreen.route) {
popUpTo(Screen.LoginScreen.route) {
inclusive = true
}
}
}
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)
}
)
}
Run Code Online (Sandbox Code Playgroud)
视图模型类:
@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
}
}
Run Code Online (Sandbox Code Playgroud)
您不应直接从可组合构建器中产生副作用或更改状态,因为这将在每次重组时执行。
相反,您可以使用副作用。在你的情况下,LaunchedEffect可以使用。
if (viewModel.loginState.value) {
LaunchedEffect(Unit) {
navController.navigate(Screen.HomeScreen.route) {
popUpTo(Screen.LoginScreen.route) {
inclusive = true
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但我认为更好的解决方案不是监听 的变化loginState,而是创建login一个挂起函数,等待它完成然后执行导航。您可以获得一个协程作用域,它将绑定到您的可组合项rememberCoroutineScope。它可以看起来像这样:
suspend fun login() : Boolean {
val response = loginRepository.login(LoginRequest(email.value, password.value))
loginRepository.saveSession(response)
return response.success ?: false
}
Run Code Online (Sandbox Code Playgroud)
另请查看 Google 工程师关于为什么不应在此答案NavController中作为参数传递的想法(根据 Navigation Compose 测试指南...)
因此,更新后您的视图将如下所示:
@Composable
fun LoginScreen(
viewModel: LoginViewModel = hiltViewModel(),
onLoggedIn: () -> Unit,
onRegister: () -> Unit,
) {
// ...
val scope = rememberCoroutineScope()
LoginFooter(
enabled,
onLoginClick = {
scope.launch {
if (viewModel.login()) {
onLoggedIn()
}
}
},
onRegisterClick = onRegister
)
// ...
}
Run Code Online (Sandbox Code Playgroud)
以及您的导航路线:
composable(route = "login") {
LoginScreen(
onLoggedIn = {
navController.navigate(Screen.HomeScreen.route) {
popUpTo(Screen.LoginScreen.route) {
inclusive = true
}
}
},
onRegister = {
navController.navigate(Screen.RegisterScreen.route)
}
)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1686 次 |
| 最近记录: |