在jetpackcompose中登录(单个事件)



我想调用"onLogin";函数并传递用户,但我无法访问";onLogin";在ViewModel中,我尝试使用mutableLiveData,但我做不到,我不知道我应该将登录传递给ViewModel,还是这是一种糟糕的做法

有一个按钮的标题是";"登录",它调用ViewModel中名为"的方法;提交";使用apollo(graphql(获取用户

SignInScreen

@Composable
fun SignInScreen(
onNavigateToSignUp:() -> Unit,
onLogin:(User) -> Unit
){
val viewModel:SignInViewModel = viewModel()

Scaffold(
bottomBar = {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.padding(bottom = 10.dp)
.fillMaxWidth()
) {
Text(text = "Don't have an account?")
Text(
text = "Sign Up.",
modifier = Modifier
.padding(start = 5.dp)
.clickable { onNavigateToSignUp() },
fontWeight = FontWeight.Bold
)
}
}
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(it),
horizontalAlignment  = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Instagram")
Spacer(modifier = Modifier.size(30.dp))
Input(viewModel.username,placeholder = "username"){
viewModel.username = it
}
Spacer(modifier = Modifier.size(20.dp))
Input(viewModel.password,placeholder = "Password"){
viewModel.password = it
}
Spacer(modifier = Modifier.size(30.dp))
Button(onClick = {viewModel.submit()},modifier = Modifier.fillMaxWidth()) {
Text(text = "Sign In")
}
}
}
}

ViewModel

class SignInViewModel(application:Application):AndroidViewModel(application) {
var username by mutableStateOf("")
var password by mutableStateOf("")

private val context = application.applicationContext
private val _user = MutableLiveData<User>(null)
val user:LiveData<User> get() = _user

fun submit(){
viewModelScope.launch {
val response = apolloClient.mutate(LoginMutation(username = Input.fromNullable(username),password = Input.fromNullable(password))).await()
_user.value = response.data?.login?.user as User
}
}
}

我就是这么做的。

1.首先,我创建了这个类,以从ViewModel到视图进行通信,并进行有状态通信,其中UI知道每次更新和通过一个实时数据显示什么。

sealed class UIState<out T>() {
class Idle() : UIState<Nothing>()
class Loading(val progress: Int = 0) : UIState<Nothing>()
class Success<out T>(val data: T?) : UIState<T>()
class Error(
val error: Throwable? = null,
val message: String? = null,
val title: String? = null
) : UIState<Nothing>()
}

2.然后当然在ViewModel中创建实时数据,也为视图创建一个不可变的副本:

private val _loginState by lazy { MutableLiveData<UIState<ResponseUser>>() }
val loginState: LiveData<UIState<ResponseUser>> = _loginState
fun performLogin(username: String, password: String) {
viewModelScope.launch {
_loginState.postValue(loading)
// your login logic here
if ("login was successful") {
_loginState.postValue(UIState.Success("your login response if needed in UI"))
} else {
_loginState.postValue(UIState.Error("some error here"))
}
}
}

3.现在在UI中,我需要将这些实时数据作为一种状态来观察,这很容易,我们有一个名为observeAsState的委托。但问题是,如果你在做导航之类的事情,你只想发生一次:

@Composable
fun LoginScreen(viewModel: LoginViewModel) {
val loginState by viewModel.loginState.observeAsState(UIState.Idle())
val hasHandledNavigation = remember { mutableStateOf(false)}
if (loginState is UIState.Success && !hasHandledNavigation.value ) {
navigateToWelcomeScreen()
else {
LoginScreenUI(loginState) { username, password ->
viewModel.performLogin(username, password)
}
}
}

4.在UI中,您需要两个文本字段和一个按钮,并且您需要记住输入的用户名和密码:

@Composable
fun LoginScreenUI(
state: UIState<ResponseUser>, onLoginButtonClicked: (username: String, password: String) -> Unit
) {
Column() {
var username by rememberSaveable { mutableStateOf("") }
OutlinedTextField(
value = username,
onValueChange = { username = it },
)
var password by rememberSaveable { mutableStateOf("") }
OutlinedTextField(
value = password,
onValueChange = { password = it },
)
Button(
onClick = {
onLoginButtonClicked(
username, password
)
}
) {
Text(text = "Login")
}
if (state is UIState.Error) {
AlertDialogComponent(state.title, state.message)
}
}
}

我希望我已经涵盖了所有内容:D

我的解决方案是使用LaunchedEffect,因为Android开发人员文档提到显示SnackBar作为一个单一时间事件的示例,代码示例与Amin Keshavarzian Answer 相同

只需将部分3更改为使用LaunchedEffect而不是标志状态hasHandledNavigation

@Composable
fun LoginScreen(viewModel: LoginViewModel) {
val loginState by viewModel.loginState.observeAsState(UIState.Idle())
LaunchedEffect(key1 = loginState) {
if (loginState is UIState.Success) 
navigateToWelcomeScreen()
}

LoginScreenUI(loginState) { username, password ->
viewModel.performLogin(username, password)

}
}

最新更新