[英]How to resolve infinite loop causing by state change in android compose with firebase authentication
I have an application with sign in via email and password with firebase.我有一个应用程序,通过 email 登录,密码为 firebase。 I'm using jetpack compose with MVVM and clean architecture.
我正在使用带有 MVVM 和干净架构的 jetpack compose。
When getting from firebase that login was done I'm getting true in the view model and then listening to this state change in the composable.当从 firebase 获取登录已完成时,我在视图 model 中得到了正确的结果,然后在可组合物中收听此 state 更改。
The problem is I'm always get in the when statement of the sign in state and it cause an infinite loop that always navigating to the next composable.问题是我总是在 state 中出现符号的 when 语句,它会导致一个无限循环,总是导航到下一个可组合。
Copying here the specific line from the view:在此处复制视图中的特定行:
when (val response = viewModel.signInState.value) {
is Response.Loading -> LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
is Response.Success -> if (response.data) {
navController?.navigate(Screen.HomeScreen.route)
}
is Response.Error -> LaunchedEffect(Unit) {
scaffoldState.snackbarHostState.showSnackbar("Error signing out. ${response.message}", "", SnackbarDuration.Short)
}
}
My View:我的观点:
@Composable
fun LoginScreen(
navController: NavController?,
viewModel: LoginViewModel = hiltViewModel()
) {
val scaffoldState = rememberScaffoldState()
Scaffold(scaffoldState = scaffoldState) {
Column (
modifier = Modifier
.fillMaxHeight()
.background(
Color.White
),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
){
var email = viewModel.email
var pass = viewModel.password
var passwordVisibility = viewModel.passwordVisibility
TitleView(title = "SIGN IN", topImage = Icons.Filled.Person, modifier = Modifier)
Spacer(modifier = Modifier.padding(20.dp))
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
) {
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
value = email.value,
onValueChange = viewModel::setEmail,
label = { Text( "Email")},
placeholder = { Text("Password") }
)
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
value = pass.value,
onValueChange = viewModel::setPassword,
label = { Text( "Password")},
placeholder = { Text("Password") },
visualTransformation = if (passwordVisibility.value) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
val image = if (passwordVisibility.value)
Icons.Filled.Visibility
else Icons.Filled.VisibilityOff
IconButton(onClick = {
viewModel.setPasswordVisibility(!passwordVisibility.value)
}) {
Icon(imageVector = image, "")
}
}
)
Spacer(modifier = Modifier.padding(5.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.border(
width = 2.dp,
brush = SolidColor(Color.Yellow),
shape = RoundedCornerShape(size = 10.dp)
),
onClick = {
viewModel.signInWithEmailAndPassword()
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.White
)
) {
Text(
text = "SIGN IN",
textAlign = TextAlign.Center,
fontSize = 20.sp,
style = TextStyle(
fontWeight = FontWeight.Thin
)
)
}
Spacer(modifier = Modifier.padding(5.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.border(
width = 2.dp,
brush = SolidColor(Color.Yellow),
shape = RoundedCornerShape(size = 10.dp)
),
onClick = {
viewModel.forgotPassword()
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.White
)
) {
Text(
text = "FORGOT PASSWORD",
textAlign = TextAlign.Center,
fontSize = 20.sp,
style = TextStyle(
fontWeight = FontWeight.Thin
)
)
}
}
}
}
when (val response = viewModel.signInState.value) {
is Response.Loading -> LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
is Response.Success -> if (response.data) {
navController?.navigate(Screen.HomeScreen.route)
}
is Response.Error -> LaunchedEffect(Unit) {
scaffoldState.snackbarHostState.showSnackbar("Error signing out. ${response.message}", "", SnackbarDuration.Short)
}
}
when (val response = viewModel.forgotState.value) {
is Response.Loading -> LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
is Response.Success -> if (response.data) {
LaunchedEffect(Unit){
scaffoldState.snackbarHostState.showSnackbar("Please click the link in the verification email sent to you", "", SnackbarDuration.Short)
}
}
is Response.Error -> LaunchedEffect(Unit){
scaffoldState.snackbarHostState.showSnackbar("Error signing out. ${response.message}", "", SnackbarDuration.Short)
}
}
}
My View model:我的看法 model:
@HiltViewModel
class LoginViewModel @Inject constructor(
private val useCases: UseCases
) : ViewModel() {
private val _signInState = mutableStateOf<Response<Boolean>>(Response.Success(false))
val signInState: State<Response<Boolean>> = _signInState
private val _forgotState = mutableStateOf<Response<Boolean>>(Response.Success(false))
val forgotState: State<Response<Boolean>> = _forgotState
private val _email = mutableStateOf("")
val email = _email
fun setEmail(email: String) {
_email.value = email
}
private val _password = mutableStateOf("")
val password = _password
fun setPassword(password: String) {
_password.value = password
}
private val _passwordVisibility = mutableStateOf(false)
val passwordVisibility = _passwordVisibility
fun setPasswordVisibility(passwordVisibility: Boolean) {
_passwordVisibility.value = passwordVisibility
}
fun signInWithEmailAndPassword() {
viewModelScope.launch {
useCases.signInWithEmailAndPassword(_email.value, _password.value).collect { response ->
_signInState.value = response
}
}
}
fun forgotPassword() {
viewModelScope.launch {
useCases.forgotPassword(_email.value).collect { response ->
_forgotState.value = response
}
}
}
}
My Sign in function:我的登录 function:
override suspend fun signInWithEmailAndPassword(
email: String,
password: String
) = flow {
try {
emit(Response.Loading)
val result = auth.signInWithEmailAndPassword(email, password).await()
if (result.user != null)
emit(Response.Success(true))
else
emit(Response.Success(false))
} catch (e: Exception) {
emit(Response.Error(e.message ?: ERROR_MESSAGE))
}
}
Navigation is also a side effect , after each frame of the animation you will again navigate to your destination.导航也是一个副作用,在 animation 的每一帧之后,您将再次导航到您的目的地。 Change your block to:
将您的块更改为:
when (val response = viewModel.signInState.value) {
is Response.Loading -> LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
is Response.Success -> if (response.data) LaunchedEffect(response.data){ navController?.navigate(Screen.HomeScreen.route) }
is Response.Error -> LaunchedEffect(Unit) {
scaffoldState.snackbarHostState.showSnackbar("Error signing out. ${response.message}", "", SnackbarDuration.Short)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.