Criar um layout de detalhes e listas

O padrão de detalhes e listas é um padrão de interface que consiste em um layout de dois painéis em que um apresenta uma lista de itens e outro mostra os detalhes dos itens selecionados na lista.

O padrão é particularmente útil para aplicativos que fornecem informações detalhadas sobre elementos de grandes coleções, por exemplo, um cliente de e-mail que tem uma lista de e-mails e o conteúdo detalhado de cada mensagem. A lista-detalhe também pode ser usada para caminhos menos críticos, como dividir as preferências do app em uma lista de categorias com as preferências de cada categoria no painel de detalhes.

Um painel de detalhes mostrado ao lado da página de lista.
Figura 1. Quando há espaço suficiente na tela, o painel de detalhes é mostrado ao lado do painel de lista.
Depois que um item é selecionado, o painel de detalhes ocupa a tela inteira.
Figura 2. Quando o tamanho da tela é limitado, o painel de detalhes (já que um item foi selecionado) ocupa todo o espaço.

Implementar o padrão de detalhes de lista com NavigableListDetailPaneScaffold

O NavigableListDetailPaneScaffold é um elemento combinável que simplifica a implementação de um layout de lista-detalhe no Jetpack Compose. Ele encapsula ListDetailPaneScaffold e adiciona navegação integrada e animações de volta preditiva.

Um scaffold de detalhes de lista oferece suporte a até três painéis:

  1. Painel de lista: mostra uma coleção de itens.
  2. Painel de detalhes: mostra os detalhes de um item selecionado.
  3. Painel extra (opcional): fornece mais contexto quando necessário.

O scaffold se adapta com base no tamanho da janela:

  • Em janelas grandes, os painéis de lista e de detalhes aparecem lado a lado.
  • Em janelas pequenas, apenas um painel fica visível por vez, alternando conforme os usuários navegam.

Declarar dependências

NavigableListDetailPaneScaffold faz parte da biblioteca de navegação adaptável do Material 3.

Adicione as três dependências relacionadas a seguir ao arquivo build.gradle do seu app ou módulo:

Kotlin

implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")

Groovy

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • adaptativa: elementos básicos de baixo nível, como HingeInfo e Posture
  • adaptive-layout: layouts adaptáveis, como ListDetailPaneScaffold e SupportingPaneScaffold
  • adaptive-navigation: elementos combináveis para navegar dentro e entre painéis, além de layouts adaptáveis que oferecem suporte à navegação por padrão, como NavigableListDetailPaneScaffold e NavigableSupportingPaneScaffold.

Verifique se o projeto inclui a versão 1.1.0-beta1 do compose-material3-adaptive ou mais recente.

Ativar o gesto de volta preditivo

Para ativar as animações de volta preditiva no Android 15 ou versões anteriores, é necessário ativar o suporte ao gesto de volta preditiva. Para ativar, adicione android:enableOnBackInvokedCallback="true" à tag <application> ou às tags <activity> individuais no arquivo AndroidManifest.xml. Para mais informações, consulte Ativar o gesto de volta preditivo.

Quando o app for direcionado ao Android 16 (nível 36 da API) ou versões mais recentes, a volta preditiva será ativada por padrão.

Uso básico

Implemente NavigableListDetailPaneScaffold da seguinte maneira:

  1. Use uma classe que represente o conteúdo selecionado. Use uma classe Parcelable para salvar e restaurar o item da lista selecionado. Use o plug-in kotlin-parcelize para gerar o código.
  2. Crie um ThreePaneScaffoldNavigator com rememberListDetailPaneScaffoldNavigator.

Esse navegador é usado para se mover entre os painéis de lista, detalhes e extras. Ao declarar um tipo genérico, o navegador também rastreia o estado do scaffold (ou seja, qual MyItem está sendo mostrado). Como esse tipo é parcelável, o estado pode ser salvo e restaurado pelo navegador para processar automaticamente mudanças de configuração.

  1. Transmita o navegador para o elemento combinável NavigableListDetailPaneScaffold.

  2. Forneça sua implementação do painel de lista ao NavigableListDetailPaneScaffold. Use AnimatedPane para aplicar as animações padrão do painel durante a navegação. Em seguida, use ThreePaneScaffoldNavigator para navegar até o painel de detalhes, ListDetailPaneScaffoldRole.Detail, e mostrar o item transmitido.

  3. Inclua a implementação do painel de detalhes em NavigableListDetailPaneScaffold.

Quando a navegação é concluída, currentDestination contém o painel para o qual seu app navegou, incluindo o conteúdo exibido nele. A propriedade contentKey é do mesmo tipo especificado na chamada original. Assim, você pode acessar todos os dados que precisa mostrar.

  1. Se quiser, mude o defaultBackBehavior em NavigableListDetailPaneScaffold. Por padrão, NavigableListDetailPaneScaffold usa PopUntilScaffoldValueChange para defaultBackBehavior.

Se o app exigir um padrão de navegação para trás diferente, você poderá substituir esse comportamento especificando outra opção de BackNavigationBehavior.

BackNavigationBehavior opções

A seção a seguir usa o exemplo de um app de e-mail com uma lista de e-mails em um painel e uma visualização detalhada no outro.

Esse comportamento se concentra em mudanças na estrutura geral do layout. Em uma configuração de vários painéis, mudar o conteúdo do e-mail no painel detalhado não altera a estrutura do layout. Portanto, o botão "Voltar" pode sair do app ou do gráfico de navegação atual porque não há uma mudança de layout para reverter no contexto atual. Em um layout de painel único, pressionar "Voltar" vai pular as mudanças de conteúdo na visualização de detalhes e retornar à visualização de lista, já que isso representa uma mudança clara de layout.

Confira estes exemplos:

  • Vários painéis:você está visualizando um e-mail (item 1) no painel de detalhes. Clicar em outro e-mail (Item 2) atualiza o painel de detalhes, mas os painéis de lista e detalhes permanecem visíveis. Ao pressionar "Voltar", você pode sair do app ou do fluxo de navegação atual.
  • Painel único:você vê o item 1 e depois o item 2. Ao pressionar "Voltar", você retorna diretamente ao painel da lista de e-mails.

Use isso quando quiser que os usuários percebam transições de layout distintas a cada ação de retorno.

Mudança no valor da navegação.
PopUntilContentChange

Esse comportamento prioriza o conteúdo mostrado. Se você abrir o item 1 e depois o item 2, pressionar "Voltar" vai reverter para o item 1, independente do layout.

Confira estes exemplos:

  • Multipainel:você vê o item 1 no painel de detalhes e clica no item 2 na lista. O painel de detalhes é atualizado. Se você pressionar "Voltar", o painel de detalhes será restaurado para Item 1.
  • Painel único:a mesma reversão de conteúdo ocorre.

Use isso quando o usuário espera voltar ao conteúdo visualizado anteriormente com a ação de voltar.

a transição entre dois painéis de detalhes
PopUntilCurrentDestinationChange

Esse comportamento remove a pilha de retorno até que o destino de navegação atual mude. Isso se aplica igualmente a layouts de painel único e múltiplos.

Confira estes exemplos:

Não importa se você está em um layout de painel único ou múltiplo, pressionar "Voltar" sempre move o foco do elemento de navegação destacado para o destino anterior. No nosso app de e-mail, isso significa que a indicação visual do painel selecionado vai mudar.

Use isso quando for crucial para a experiência do usuário manter uma indicação visual clara da navegação atual.

navegar entre os painéis de detalhes e de lista
PopLatest

Essa opção remove apenas o destino mais recente da backstack. Use essa opção para navegação para trás sem pular estados intermediários.

Depois de implementar essas etapas, seu código vai ficar parecido com este:

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
val scope = rememberCoroutineScope()

NavigableListDetailPaneScaffold(
    navigator = scaffoldNavigator,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    scope.launch {
                        scaffoldNavigator.navigateTo(
                            ListDetailPaneScaffoldRole.Detail,
                            item
                        )
                    }
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            scaffoldNavigator.currentDestination?.contentKey?.let {
                MyDetails(it)
            }
        }
    },
)