Ejemplo práctico 10: Login en un cuadro de diálgo
Objetivo
Este ejemplo permite crear un AlertDialog
personalizado para solicitar usuario y contraseña.
Recursos en string.xml
Como en la versión anterior, se tratará de evitar lo máximo posible el hardcoded text, aunque en ocasiones, verás que se omite por razones didácticas.
1<resources>
2 <string name="app_name">ExampleT2_10</string>
3
4 <string name="txt_title">Login</string>
5 <string name="txt_user">Usuario</string>
6 <string name="txt_password">Contraseña</string>
7
8 <string name="txt_login_error">Credenciales incorrectas</string>
9 <string name="txt_login_ok">Credenciales correctas</string>
10</resources>
Compose LoginDialog
Se creará el siguiente compose para mostrar el cuadro de diálogo personalizado, en esta ocasión se utiliza AlertDialog
ya que la personalización es mínima y simplifica el código, pero para los cuadros de diálogo personalizados se recomienda el uso de Dialog
.
1@Composable
2fun LoginDialog(onLogin: (String, String) -> Unit = { _, _ -> }) {
3 val ctxt = LocalContext.current
4 val openDialog = remember { mutableStateOf(false) }
5 var user by remember { mutableStateOf("") }
6 var pass by remember { mutableStateOf("") }
7
8 Box(
9 modifier = Modifier.fillMaxSize(),
10 contentAlignment = Alignment.Center
11 ) {
12 Button(onClick = { openDialog.value = true }) {
13 Text(text = ctxt.getString(R.string.txt_title))
14 }
15
16 if (openDialog.value) {
17 AlertDialog(
18 onDismissRequest = { openDialog.value = true }, // Se mantiene el diálogo abierto.
19 title = { Text(text = ctxt.getString(R.string.txt_title)) },
20 text = {
21 Column {
22 OutlinedTextField(
23 value = user,
24 onValueChange = { user = it },
25 singleLine = true,
26 label = { Text(ctxt.getString(R.string.txt_user)) }
27 )
28 OutlinedTextField(
29 value = pass,
30 onValueChange = { pass = it },
31 singleLine = true,
32 label = { Text(ctxt.getString(R.string.txt_password)) },
33 visualTransformation = PasswordVisualTransformation()
34 )
35 }
36 },
37 confirmButton = {
38 TextButton(onClick = {
39 if (user.isNotBlank() && pass.isNotBlank()) {
40 onLogin(user, pass)
41 openDialog.value = false
42 // Limpiar los campos después del inicio de sesión.
43 user = ""
44 pass = ""
45 }
46 }) {
47 Text(ctxt.getString(android.R.string.ok))
48 }
49 },
50 dismissButton = {
51 TextButton(onClick = {
52 openDialog.value = false
53 user = ""
54 pass = ""
55 }) {
56 Text(ctxt.getString(android.R.string.cancel))
57 }
58 }
59 )
60 }
61 }
62}
Observa el uso de visualTransformation
en el OutlinedTextField
, este permite la ocultación del password.
Compose MainScreen
Siguiendo con la reutilización y actualización del estado, se creará el siguiente composable para mostrar el botón de login o el mensaje de login correcto.
1@Composable
2fun MainScreen() {
3 var isLoggedIn by remember { mutableStateOf(false) }
4 val ctxt = LocalContext.current
5
6 if (!isLoggedIn) {
7 // Se muestra el diálogo de inicio de sesión
8 LoginDialog(
9 onLogin = { user, pass ->
10 // Aquí se maneja la lógica de inicio de sesión
11 // Por ejemplo, verificar las credenciales
12 if (user == "admin" && pass == "1234") {
13 isLoggedIn = true // Simulación de inicio de sesión exitoso
14 println("Inicio de sesión correcto. Usuario: $user, Contraseña: $pass")
15 } else {
16 // Se muestra un mensaje de error o manejar el fallo de inicio de sesión
17 Toast.makeText(
18 ctxt,
19 ctxt.getString(R.string.txt_login_error),
20 Toast.LENGTH_SHORT
21 ).show()
22 }
23 }
24 )
25 } else {
26 // Contenido principal de la aplicación
27 Text(text = ctxt.getString(R.string.txt_login_ok))
28 }
29}
Resultado de la MainActivity
Ahora, la actividad principal tendrá el siguiente aspecto.
1class MainActivity : ComponentActivity() {
2 @OptIn(ExperimentalMaterial3Api::class)
3 override fun onCreate(savedInstanceState: Bundle?) {
4 super.onCreate(savedInstanceState)
5 enableEdgeToEdge()
6 setContent {
7 ExampleT2_10Theme {
8 Scaffold(
9 topBar = { TopAppBar(title = { Text(getString(R.string.app_name)) }) }
10 ) { innerPadding ->
11 Box(
12 modifier = Modifier
13 .fillMaxSize()
14 .padding(innerPadding),
15 contentAlignment = Alignment.Center
16 ) {
17 MainScreen()
18 }
19 }
20 }
21 }
22 }
23}