Criando Alert Dialogs com Navigation - Jetpack Compose

Criando Alert Dialogs com Navigation - Jetpack Compose

Introdução

O Jetpack Compose é uma biblioteca de UI do Android que permite a criação de interfaces de usuário reativas e intuitivas. Um dos recursos mais importantes do Compose é o Navigation, que permite a navegação fluida entre as telas do aplicativo.

Neste artigo, vamos mostrar como criar Alert Dialogs personalizados no Navigation, para tornar a experiência do usuário mais ágil e nosso código mais eficiente e reutilizável. Abaixo, você tem um exemplo da tela que iremos criar:

Tela de uma aplicação móvel, o aplicativo possuí um título no topo escrito “destaques do dia”, o aplicativo simula um delivery de um restaurante, com uma lista vertical de cartões podendo ter a imagem, o nome, a descrição do produto e um botão no canto inferior direito do card escrito pedir. No centro da tela há uma caixa diálogo sobrepondo o conteúdo dos destaques, a caixa se refere a itens adicionados e pergunta se a pessoa deseja ir para as formas de pagamento na sacola ou continuar comprando. Abaixo, há dois botões, um com a opção de continuar compra e outro com a opção de ir para a sacola. Banner da Escola de Mobile: Matricula-se na escola de Mobile. Junte-se a uma comunidade de mais de 500 mil estudantes. Na Alura você tem acesso a todos os cursos em uma única assinatura; tem novos lançamentos a cada semana; desafios práticos. Clique e saiba mais!

Conhecendo o projeto

O projeto a ser utilizado é um App de restaurante que oferece diversas formas de visualização do cardápio, através de uma barra inferior de navegação com destaques, menu e bebidas, podendo também efetuar pedidos e escolher formas de pagamento, conforme exemplo abaixo:

Gif de navegação entre as telas do aplicativo “Ristorante Panucci”, a navegação é feita pelas telas de destaques, menu, bebidas, checkout de pedidos, detalhes de produtos e pagamentos.

Esse código foi feito no curso de Jetpack Compose: navegando entre telas com o Navigation, aqui da Alura, e você pode acessar o código neste link.

Se você é novo esse assunto e ainda não tem familiaridade com o Navigation do compose, recomendo que acesse a documentação oficial de navegação e faça os cursos disponíveis na plataforma da Alura sobre o tema.

Fluxo de Dialogs otimizado com o Navigation

Vamos partir do seguinte contexto: É preciso que ao clicar no botão “pedir” na tela de destaques, apareça uma solicitação de confirmação perguntando se gostaríamos de ir direto para o pagamento ou continuar comprando. Com esse contexto, qual seria a melhor forma de implementar essa tarefa?

Podemos dizer que teríamos várias formas de implementar essa atividade, e vamos destacar as consideradas mais comuns:

  1. Navegar para uma tela completa de “Continuar comprando” com algumas opções, sem nenhum sobreposição; mas isso pode não ser necessário com poucas opções;
  2. Outra forma seria utilizar o Dialogs do Compose para questões objetivas, como confirmação ou cancelamento.

E essa última é a que vamos utilizar juntamente com algumas facilidades do Navigation, conforme exemplo abaixo:

Tela de um aplicativo, com imagens de pessoas ao fundo, ao centro da imagem há uma caixa de diálogo com o título: “Deletar imagens selecionadas?” em inglês, com uma descrição escrita: “imagens vão ser permanentemente removidas da sua conta e dos seus dispositivos sincronizados” em inglês. No canto inferior direito há dois botões, um para cancelar e outro para deletar.

Se quiser saber mais sobre Dialogs, acesse o artigo Utilizando Dialogs no Jetpack Compose para exibir mensagens ao usuário.

Agora que você já conhece nossa proposta e tem acesso ao projeto, vamos para a prática!

Implementando com o Navigation

Aqui teremos um exemplo de como fazer essa implementação, mas você pode implementar da forma que achar melhor e colocar as funcionalidades que preferir. Dito isso, vamos lá.

Criando o Dialog

Vamos começar criando o componente visual do nosso Alert Dialog:

1) Crie um composable dentro da pasta components com o nome que preferir. Aqui vou utilizar o nome DialogOrderConfirmation;

2) Você pode utilizar o composable AlertDialog do próprio compose, para estilizar mais rápido:

@Composable
fun OrderConfirmationDialog(
    onConfirmButton: () -> Unit = {},
    onDismissButton: () -> Unit = {},
    onDismissRequest: () -> Unit = {}
) {
    AlertDialog(
        onDismissRequest = { onDismissRequest() },
        confirmButton = { TextButton(onClick = { onConfirmButton() }) {
            Text(text = "Ir para sacola")
        } },
        dismissButton = { TextButton(onClick = { onDismissButton() }) {
            Text(text = "Continuar compra")
        } },
        icon = { Icon(Icons.Default.Shop, null) },
        title = { Text(text = "Itens adicionados")},
        text = { Text(text = """
            1x - Lorem ipsum dolor sit amet

            Deseja ir para as formas de pagamento na sacola ou continuar comprando? 
        """.trimIndent()) }
    )
}

3) Como resultado visual, você terá este dialog:

Layout de uma caixa diálogo, no topo háum ícone de uma sacola com um triângulo no centro, o dialog se refere a itens adicionados e pergunta se a pessoa deseja ir para as formas de pagamento na sacola ou continuar comprando. Abaixo, há dois botões, um com a opção de continuar compra e outro com a opção de ir para a sacola.

Implementando rota

Agora é preciso criar uma rota para esse Dialog:

1) Vá na pasta navigation > AppDestination.kt

2) Dentro da classe AppDestination, inclua no final do seu corpo a linha referente à rota do nosso dialog:

sealed class AppDestination(val route: String) {

        ... código omitido

    object OrderConfirmationDialog : AppDestination("orderConfirmationDialog")
}

3) Em seguida, na MainActivity dentro de NavHost ao invés de colocar a chamada decomposable(…), utilize uma específica para Dialogs no Navigation e insira nosso composable de Dialog nela:

NavHost(
    navController = navController,
    startDestination = AppDestination.Highlight.route
) {

        ... código omitido

        dialog(AppDestination.OrderConfirmationDialog.route) {
            OrderConfirmationDialog(
                onConfirmButton = { navController.navigate(AppDestination.Checkout.route) },
                onDismissButton = { navController.popBackStack() },
                onDismissRequest = { navController.popBackStack() },
                )
        }
}

É interessante destacar que quando colocamos um composable dentro da chamada de dialog(…) { }, temos alguns comportamentos interessantes para os Dialogs, como o fato de que se após navegarmos ao Checkout quando clicamos em “ir para sacola”, e ocasionalmente apertarmos para voltar dentro da tela de Checkout, não somos redirecionados para o Dialog. Isso acontece porque ele é retirado automaticamente da backStack ao utilizarmos as chamadas do Navigation corretamente.

Caso utilize a chamada de composable(…) ao invés de dialog(…), teremos alguns comportamentos que não condizem com os Dialogs, como por exemplo:

  • Ele navega para uma tela específica somente com o Dialog, ou seja, você não será capaz de ver o contexto em que o Dialog foi iniciado;
  • Caso faça o processo de voltar na tela de Checkout, ele vai voltar para a tela de Dialog, coisa que prejudica o fluxo do App e a experiência do usuário colocando muitas ações repetitivas.

Se quiser, faça o teste substituindo as chamadas e veja o que acontece.

Seguindo os passos corretamente, você chegará neste resultado:

Gif de navegação entre as telas do aplicativo “Ristorante Panucci”, a navegação é feita pelas telas de destaques, menu, bebidas, checkout de pedidos, detalhes de produtos e pagamentos. Ao clicar em pedir na tela de destaques uma caixa diálogo, no topo há um ícone de uma sacola com um triângulo no centro, o Dialog se refere a itens adicionados e pergunta se a pessoa deseja ir para as formas de pagamento na sacola ou continuar comprando. Abaixo, há dois botões, um com a opção de continuar compra e outro com a opção de ir para a sacola. Ao clicar em “continuar compra” o Dialog é fechado, porém, ao clicar em “ir para sacola” a tela de checkout com as informações do pedido é exibida.

Como uma boa prática, dentro do nosso composable HighlightsListScreen faça uma alteração semântica na ação de onNavigateToCheckout, no caso faça a alteração do nome da ação para: onNavigateToOrderConfirmation e ao invés de usar a rota para o Checkout, use a rota para o Dialog, desta forma:

composable(AppDestination.Highlight.route) {
    HighlightsListScreen(
        products = sampleProducts,
        onNavigateToDetails = { product ->
            navController.navigate(
                "${AppDestination.ProductDetails.route}/${product.id}"
            )
        },
        onNavigateToOrderConfirmation = {
            // navController.navigate(AppDestination.Checkout.route)
            navController.navigate(AppDestination.OrderConfirmationDialog.route)
        },
    )
}

Conclusão

Neste artigo, aprendemos como os Dialogs são implementados juntamente da biblioteca de Navigation do Jetpack Compose. Através dessa técnica conseguimos fazer um fluxo de navegação mais fluído tanto para a pessoa que usa o App quanto para a pessoa desenvolvedora, que conta com uma melhor usabilidade do app e a personalização mais simplificada.

Se você se interessou pelo tema e quer ser aprofundar ainda mais em Android, recomendamos a formação: Jetpack Compose: criando telas e gerenciando estados, como também o curso Jetpack Compose: Navegando entre telas com o Navigation.

Continue mergulhando com a gente e até a próxima!

Matheus Perez
Matheus Perez

Matheus é estudante de ciências da computação, apaixonado por tecnologia em geral, desenvolvedor mobile nativo (Android/iOS) com as linguagens Kotlin e Swift, entusiasta nas áreas de UX/UI. Seu principal foco é trazer ótimas experiências com tecnologia.

Veja outros artigos sobre Mobile