Componente ‘Dialog’ con Kotlin + Jetpack Compose + Material Design 3

dacape.dev
5 min readApr 29, 2023

--

Componente ‘Dialog’ con Kotlin + Jetpack Compose + Material Design 3

El componente ‘dialog’ es uno de los recursos más utilizados en el desarrollo de aplicaciones para dispositivos móviles en casi todos los sistemas de diseño. Se utiliza para muchos tipos de acciones debido a su versatilidad y facilidad de uso para los usuarios que además están muy acostumbrados a él.

Se trata de un panel que se abre mediante una animación (dependiendo del ‘framework’ que estemos usando podremos elegir entre diferentes animaciones) y que se utiliza para mostrar información puntual o para recoger datos que el usuario va a insertar.

Tipos de ‘dialog’

Existen 2 tipos y cada uno de ellos se utiliza para cosas distintas. Es importante saber cuándo debemos usar cada uno de ellos, porque los usuarios llevan muchos años usándolos de esta forma y cambiarlo podría provocar una mala experiencia de usuario:

  • Básico: se muestra en el centro de la pantalla y se utiliza para mostrar información de obligada lectura por el usuario (ya que lleva botón para cerrar) para confirmar/rechazar acciones (por ejemplo ‘¿Está seguro de borrar este registro?’) o para recoger algún dato simple del usuario, nunca para un formulario completo.
  • Pantalla completa: es un ‘dialog’ que ocupa toda la pantalla y se utiliza para recopilar información introducida por el usuario en formularios completos. Si el formulario es demasiado grande, no recomiendo usar ‘dialogs’. También se puede usar para mostrar tutoriales con slides de la aplicación y cosas así.

🚀 ‘Dialog’ con Jetpack Compose

El componente ’dialog’ de Material Design 3 ya viene integrado en Jetpack Compose bajo el nombre AlertDialog y podemos usarlo tanto a pantalla completa como con su forma básica.

A continuación, vamos a crear una aplicación sencilla con un ‘dialog’ gestionado por un view model para que se gestionen desde ahí las acciones del usuario y poder (no en este ejemplo) integrarlo con otros eventos como guardar formulario etc.

Stack tecnológico

🤖 Android/Kotlin — 🚀 Jetpack Compose — 🖌️ Material Design 3

📚 Dependencias

Lo primero es revisar las dependencias del proyecto en los archivos ‘build.gradle’. Encontraremos dos, uno perteneciente a la app global y otro al módulo que se ha generado. Lo ideal, es que como mínimo tengáis estas versiones definidas:

  • compose_version = ‘1.1.1’
  • material3_version = ‘material3:1.0.0-alpha02’
  • lifecycle-viewmodel-compose = ‘2.6.1’

🖌️ View model

Creamos un view model para gestionar nuestra única pantalla. En él, vamos a tener una variable del tipo mutableStateOf boolean que va a indicar cuando abrir/cerrar el diálogo.

Además, tendremos una función para gestionar los eventos de apertura/cierre y una factoría para nuestro view model:

class MainViewModel(application: Application): ViewModel() {

var openDialog by mutableStateOf(false)

init {

}

fun onEvent(event: Event) {
when (event) {
is Event.OpenDialog -> {
openDialog = true
}
is Event.CloseDialog -> {
openDialog = false
}
}
}

}


class MainViewModelFactory (private val application: Application) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MainViewModel(application) as T
}
}

Debemos crear una clase sellada con los objetos de cada evento que vamos a gestionar:

sealed class Event {
object OpenDialog: Event()
object CloseDialog: Event()
}

🪧 Componente ‘Dialog’

Ahora sí, creamos el ‘composable’ con 2 elementos, primero un botón que acciona el evento ‘openDialog’, sin más y un AlertDialog sencillo que seria del tipo básico, vamos a comentar todos los elementos que tiene disponiblesY:

  • onDismissRequest: es la acción que se va a realizar frente a cualquier cierre del panel por defecto, es decir, cuando el usuario pulsa por ejemplo el ‘back button’ o cuando clicka fuera del ‘dialog’. Opcional.
  • icon: se trata del icono que aparecerá en la parte superior. Opcional.
  • tittle: titulo del ‘dialog’, aparecerá en la parte superior. Opcional.
  • text: texto que aparecerá en la parte central. Opcional.
  • confirmButton: forma y acción del botón para confirmar.
  • dismissButton: forma y acción del botón para rechazar. Opcional.
Dialog preview

Además existen otros elementos para cambiar el color del icono, texto etc y demás estilos. También se pueden cambiar otras propiedades del dialogo como las opciones de dismiss (cuando se clicka fuera etc).

@Composable
fun Dialog(viewModel: MainViewModel) {

Box(contentAlignment = Alignment.Center) {
Button(
onClick = { viewModel.onEvent(Event.OpenDialog) }
) {
Icon(
Icons.Filled.Favorite,
contentDescription = "Localized description",
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text("Open dialog")
}
}


if (viewModel.openDialog) {
AlertDialog(
onDismissRequest = {
viewModel.onEvent(Event.CloseDialog)
},
icon = { Icon(Icons.Filled.Add, contentDescription = null) },
title = {
Text(text = "New item (example)")
},
text = {
Text(
"Are you sure you want to add a new item? (example)"
)
},
confirmButton = {
TextButton(
onClick = {
viewModel.onEvent(Event.CloseDialog)
}
) {
Text("Confirm")
}
},
dismissButton = {
TextButton(
onClick = {
viewModel.onEvent(Event.CloseDialog)
}
) {
Text("Dismiss")
}
}
)
}
}

Por último, obtenemos nuestro view model con la factoría y se lo pasamos al ‘composable’ dialog que hemos creado:

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Dialogsjetpackmaterial3Theme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {

val owner = LocalViewModelStoreOwner.current

owner?.let {
val viewModel: MainViewModel = viewModel(
it,
"MainViewModel",
MainViewModelFactory(
LocalContext.current.applicationContext
as Application
)
)

Dialog(viewModel)
}


}
}
}
}
}

⭐️ Conclusiones

Sin duda, diría que los ‘dialogs’ son uno de los recursos más utilizados para mejorar la experiencia de usuario. Mi recomendación es usarlos cuando la aplicación necesite recoger algún dato del usuario, de una forma más dinámica y sencilla (nunca formularios muy complejos) o cuando tengamos que mostrar información al usuario de obligada lectura.

No debemos abusar del uso de este componente para mensajes meramente informativos y no relevantes como ‘Registro guardado correctamente’, ni para mensajes de error en los que el usuario no puede hacer nada para corregirlo como puede ser ‘Error desconocido’ etc.

Puedes encontrar todo el código de este artículo en mi repositorio de GitHub, también puedes votar positivamente este artículo aquí en Medium o darle una ⭐ a mi repositorio de GitHub 😊.

🔎 Referencias

--

--