Overview
The Login Screen provides a secure authentication interface for users to access the NASA Explorer application. It features email and password input fields with comprehensive validation, loading states, and error handling.
Key Features
Email Validation Validates email format using Android’s EMAIL_ADDRESS pattern matcher
Password Security Enforces minimum 6-character password requirement
Loading States Displays circular progress indicator during authentication
Error Handling Shows toast messages for validation and authentication errors
Screen Components
Main UI Elements
The login screen consists of the following visual components:
Logo Image : Planet icon (250x250dp) with rounded corners and white tint
Email TextField : Outlined text field for email input
Password TextField : Outlined text field for password input with secure entry
Sign Up Link : Clickable text to navigate to registration screen
Login Button : Full-width button at the bottom to submit credentials
Progress Indicator : Circular loading indicator shown during authentication
All text fields use white color styling to contrast against the black background theme.
Architecture
LoginScreen Composable
The main composable function that renders the login interface:
@Composable
fun LoginScreen (
loginScreenViewModel: LoginScreenViewModel = hiltViewModel (),
onNavigationToHome: () -> Unit,
onNavigationToSignUp: () -> Unit
) {
val isLoading by loginScreenViewModel.isLoading. collectAsState ()
val errorMessage by loginScreenViewModel.errorMessage. collectAsState ()
// Error message handling
if (errorMessage. isNotEmpty ()) {
Toast. makeText (LocalContext.current, errorMessage, Toast.LENGTH_LONG). show ()
loginScreenViewModel. resetErrorMessage ()
}
Column (
modifier = Modifier
. fillMaxSize ()
. background (Color.Black),
Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
// UI components
}
}
ViewModel State Management
The LoginScreenViewModel manages authentication state and business logic:
@HiltViewModel
class LoginScreenViewModel @Inject constructor (
private val authService: AuthService
) : ViewModel () {
private var _isLoading = MutableStateFlow < Boolean >( false )
val isLoading: StateFlow < Boolean > = _isLoading
private var _errorMessage = MutableStateFlow < String >( "" )
val errorMessage: StateFlow < String > = _errorMessage
fun login (email: String , password: String , onNavigateToHome: () -> Unit) {
// Validation and authentication logic
}
}
Validation Logic
The screen performs comprehensive validation before attempting authentication:
Empty Field Check
Verifies that both email and password fields are not blank if (email. isBlank () || password. isBlank ()) {
_errorMessage. value = "Los campos no pueden estar vacios"
return
}
Email Format Validation
Validates email format using Android’s pattern matcher private fun isValidEmail (email: String ): Boolean {
return android.util.Patterns.EMAIL_ADDRESS. matcher (email). matches ()
}
Password Length Check
Ensures password meets minimum length requirement if (password.length < 6 ) {
_errorMessage. value = "La contraseña debe tener al menos 6 caracteres"
return
}
Authentication Flow
Login Process
The authentication flow uses Kotlin coroutines for asynchronous operations:
fun login (email: String , password: String , onNavigateToHome: () -> Unit) {
viewModelScope. launch {
_isLoading. value = true
try {
val result = withContext (Dispatchers.IO) {
authService. login (email = email, password = password)
}
if (result != null ) {
onNavigateToHome ()
} else {
_errorMessage. value = "Error de autenticación.Por favor, revisa tus credenciales."
}
} catch (e: Exception ) {
_errorMessage. value = "Correo o contraseña no válidos."
} finally {
_isLoading. value = false
}
}
}
Authentication operations run on Dispatchers.IO to prevent blocking the main UI thread.
State Management
StateFlow Properties
The screen observes the following state flows:
State Type Purpose isLoadingStateFlow<Boolean>Controls progress indicator visibility errorMessageStateFlow<String>Stores validation or authentication error messages
State Collection
State is collected using Compose’s collectAsState() extension:
val isLoading by loginScreenViewModel.isLoading. collectAsState ()
val errorMessage by loginScreenViewModel.errorMessage. collectAsState ()
Navigation
Navigation Actions
The screen supports two navigation callbacks:
onNavigationToHome
onNavigationToSignUp
// Called after successful authentication
onNavigationToHome: () -> Unit
Sign Up Link Implementation
Text (
text = "Registrarse" ,
modifier = Modifier
. clickable { onNavigationToSignUp () }
. align (Alignment.End)
. padding (end = 56 .dp, top = 8 .dp),
color = Color.White
)
Error Handling
Error Message Display
Errors are displayed using Android Toast messages:
if (errorMessage. isNotEmpty ()) {
Toast. makeText (LocalContext.current, errorMessage, Toast.LENGTH_LONG). show ()
loginScreenViewModel. resetErrorMessage ()
}
Error Types
Message : “Los campos no pueden estar vacios”Triggered when either email or password field is blank.
Message : “El correo no es válido”Triggered when email format doesn’t match standard pattern.
Message : “La contraseña debe tener al menos 6 caracteres”Triggered when password is shorter than 6 characters.
Message : “Correo o contraseña no válidos”Triggered when authentication fails with invalid credentials.
Dependencies
Injection & State
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.compose.runtime.collectAsState
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
Compose UI
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.foundation.layout. *
File Location
app/src/main/java/com/ccandeladev/nasaexplorer/ui/loginscreen/
├── LoginScreen.kt
└── LoginScreenViewModel.kt
Best Practice : The screen follows MVVM architecture with clear separation between UI (LoginScreen.kt) and business logic (LoginScreenViewModel.kt).