Skip to content

Instantly share code, notes, and snippets.

@crisu83
Last active October 5, 2021 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crisu83/3f3fb978f59ddbda7d74d7a329a4bd44 to your computer and use it in GitHub Desktop.
Save crisu83/3f3fb978f59ddbda7d74d7a329a4bd44 to your computer and use it in GitHub Desktop.
LoginScreen
@ExperimentalComposeUiApi
@Composable
fun LoginScreen(
viewModel: LoginViewModel,
navigateToOverview: () -> Unit = {},
onBack: () -> Unit = {},
) {
val uiState by viewModel.uiState.collectAsState()
val (username, setUsername) = viewModel.username
val (password, setPassword) = viewModel.password
LoginScreen(
resourceState = uiState.resourceState,
username = username,
setUsername = setUsername,
password = password,
setPassword = setPassword,
onSubmit = { viewModel.onSubmit() },
isSubmitting = uiState.isSubmitting,
onBack = onBack
)
LaunchedEffect(uiState.shouldRedirect) {
if (uiState.shouldRedirect) {
navigateToOverview()
}
}
}
@ExperimentalComposeUiApi
@Composable
private fun LoginScreen(
resourceState: ResourceState = ResourceState.Idle,
username: String = "",
setUsername: (String) -> Unit = {},
password: String = "",
setPassword: (String) -> Unit = {},
onSubmit: () -> Unit = {},
isSubmitting: Boolean = false,
onBack: () -> Unit = {},
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val focusRequester = remember { FocusRequester() }
Screen(
topBar = {
TopBar(title = stringResource(id = R.string.log_in), isElevated = false)
},
titleBar = {
TitleBar(
title = stringResource(id = R.string.enter_username_and_password),
onBack = onBack
)
},
) {
LoginForm(
username = username,
setUsername = setUsername,
password = password,
setPassword = setPassword,
onSubmit = onSubmit,
isSubmitting = isSubmitting,
focusManager = focusManager,
focusRequester = focusRequester,
)
}
LaunchedEffect(resourceState) {
when (resourceState) {
ResourceState.Idle -> {
focusRequester.requestFocus()
keyboardController?.show()
}
ResourceState.Loading -> {
focusManager.clearFocus()
keyboardController?.hide()
}
ResourceState.Success -> {
}
ResourceState.Error -> {
focusRequester.requestFocus()
}
}
}
}
@ExperimentalComposeUiApi
@Composable
private fun LoginForm(
username: String,
setUsername: (String) -> Unit,
password: String,
setPassword: (String) -> Unit,
onSubmit: () -> Unit = {},
isSubmitting: Boolean = false,
focusManager: FocusManager,
focusRequester: FocusRequester,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = Dimen.ExtraLarge, vertical = Dimen.Small),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(Modifier.padding(bottom = Dimen.Medium)) {
TextField(
modifier = Modifier.focusRequester(focusRequester),
label = stringResource(id = R.string.username),
value = username,
onValueChanged = setUsername,
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Next,
keyboardType = KeyboardType.Email,
),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) },
)
)
TextField(
label = stringResource(id = R.string.password),
value = password,
onValueChanged = setPassword,
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Password,
),
keyboardActions = KeyboardActions(
onDone = { onSubmit() },
)
)
}
Button(
text = stringResource(id = R.string.log_in),
enabled = !isSubmitting,
onClick = onSubmit,
)
}
}
@ExperimentalComposeUiApi
@Preview(
name = "Light Mode"
)
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
name = "Dark Mode"
)
@Composable
fun PreviewLoginScreen() {
LoginScreen()
}
data class LoginUiState(
val resourceState: ResourceState = ResourceState.Idle,
val shouldRedirect: Boolean = false,
) {
val isSubmitting: Boolean get() = resourceState == ResourceState.Loading
}
class LoginViewModel(private val repository: AccountsRepository) : ViewModel() {
private val _uiState = MutableStateFlow(LoginUiState())
val uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()
val username = mutableStateOf("")
val password = mutableStateOf("")
fun onSubmit() {
viewModelScope.launch(Dispatchers.IO) {
_uiState.update { it.copy(resourceState = ResourceState.Loading) }
val result = repository.login(username.value, password.value)
_uiState.update {
if (result is Result.Success) {
it.copy(resourceState = ResourceState.Success, shouldRedirect = true)
} else {
it.copy(resourceState = ResourceState.Error)
}
}
}
}
companion object {
fun provideFactory(
accountsRepository: AccountsRepository
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return LoginViewModel(accountsRepository) as T
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment