繁体   English   中英

如何解决由 state 在 android 中更改导致的无限循环与 firebase 认证组合

[英]How to resolve infinite loop causing by state change in android compose with firebase authentication

我有一个应用程序,通过 email 登录,密码为 firebase。 我正在使用带有 MVVM 和干净架构的 jetpack compose。

当从 firebase 获取登录已完成时,我在视图 model 中得到了正确的结果,然后在可组合物中收听此 state 更改。

问题是我总是在 state 中出现符号的 when 语句,它会导致一个无限循环,总是导航到下一个可组合。

在此处复制视图中的特定行:

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)
        }
    }

我的观点:

@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)
        }
    }
}

我的看法 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
            }
        }
    }
}

我的登录 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))
        }
    }

导航也是一个副作用,在 animation 的每一帧之后,您将再次导航到您的目的地。 将您的块更改为:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM