Criar um layout de detalhes e listas

O padrão de detalhes e listas é um layout de dois painéis em que um painel apresenta uma lista de itens e outro exibe 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. O detalhe da lista também pode ser usado 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 o tamanho da tela está disponível, o painel de detalhes é mostrado ao lado do painel de lista.
Depois que um item é selecionado, o painel de detalhes ocupa toda a tela.
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 e listas com NavigableListDetailPaneScaffold

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

Um esqueleto de detalhes e listas 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): oferece mais contexto quando necessário.

O esqueleto 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 abaixo ao arquivo build.gradle do 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'
  • adaptável: 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, bem como 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 preditivo. 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 é direcionado ao Android 16 (nível 36 da API) ou versões mais recentes, a volta preditiva é ativada por padrão.

Uso básico

Implemente NavigableListDetailPaneScaffold desta forma:

  1. Use uma classe que represente o conteúdo selecionado. Use uma classe Parcelable para oferecer suporte ao salvamento e à restauração do item de lista selecionado. Use o plug-in kotlin-parcelize para gerar o código.
  2. Crie um ThreePaneScaffoldNavigator com rememberListDetailPaneScaffoldNavigator.

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

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

  2. Forneça a implementação do painel de lista para o 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 o app navegou, incluindo o conteúdo exibido no painel. A propriedade contentKey é do mesmo tipo especificado na chamada original para que você possa 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 de volta diferente, você poderá substituir esse comportamento especificando outra opção 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 com vários painéis, mudar o conteúdo do e-mail no painel de detalhes não altera a estrutura de layout subjacente. 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" pula as mudanças de conteúdo na visualização de detalhes e retorna à 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 de detalhes continuam visíveis. Apertar "Voltar" pode sair do app ou do fluxo de navegação atual.
  • Painel único:você visualiza o item 1 e, em seguida, o item 2. Ao pressionar "Voltar", você volta diretamente ao painel da lista de e-mails.

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

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

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

Confira estes exemplos:

  • Vários painéis:você visualiza 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 o item 1.
  • Painel único:ocorre a mesma reversão de conteúdo.

Use isso quando o usuário espera retornar ao conteúdo visualizado anteriormente com a ação "Voltar".

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

Esse comportamento abre a backstack até que o destino de navegação atual mude. Isso se aplica igualmente a layouts de painel único e de vários painéis.

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 essa opção quando manter uma indicação visual clara da navegação atual for crucial para a experiência do usuário.

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 voltar sem pular estados intermediários.

Depois de implementar essas etapas, o 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)
            }
        }
    },
)