Criar um layout de painel de suporte

O layout do painel de suporte mantém o foco do usuário no conteúdo principal do app enquanto mostra informações de suporte relevantes. Por exemplo, o painel principal pode mostrar detalhes sobre um filme, enquanto o painel de suporte lista filmes semelhantes, filmes do mesmo diretor ou obras com os mesmos atores.

Para mais detalhes, consulte as diretrizes do painel de suporte do Material 3.

Implementar um painel de suporte com o NavigableSupportingPaneScaffold

NavigableSupportingPaneScaffold é um elemento combinável que simplifica a implementação de um layout de painel de suporte no Jetpack Compose. Ele envolve SupportingPaneScaffold e adiciona navegação integrada e tratamento de volta preditiva.

Um esqueleto de painel de suporte oferece suporte a até três painéis:

  • Painel principal: exibe o conteúdo principal.
  • Painel de suporte: fornece contexto ou ferramentas adicionais relacionadas ao painel principal.
  • Painel extra (opcional): usado para conteúdo complementar quando necessário.

O esqueleto se adapta com base no tamanho da janela:

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

    O conteúdo principal ocupa a maior parte da tela com o conteúdo de apoio ao lado.
    Figura 1. Layout do painel de suporte.

Adicionar dependências

NavigableSupportingPaneScaffold faz parte da biblioteca de layout 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
  • layout-adaptável: layouts adaptáveis, como ListDetailPaneScaffold e SupportingPaneScaffold.
  • navegação-adaptável: 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> [tag ou android:enableOnBackInvokedCallback="true" à tag <application> ou <activity> individual no arquivo AndroidManifest.xml.

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

Criar um navegador

Em janelas pequenas, apenas um painel é exibido por vez. Use um ThreePaneScaffoldNavigator para alternar entre painéis. Crie uma instância do navegador com rememberSupportingPaneScaffoldNavigator.

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

Transmitir o navegador para o esqueleto

O esqueleto requer um ThreePaneScaffoldNavigator, que é uma interface que representa o estado do esqueleto, o ThreePaneScaffoldValue e um PaneScaffoldDirective.

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = { /*...*/ },
    supportingPane = { /*...*/ },
)

O painel principal e o de suporte são elementos combináveis que contêm seu conteúdo. Use AnimatedPane para aplicar as animações padrão do painel durante a navegação. Use o valor do scaffold para verificar se o painel de suporte está oculto. Se estiver, mostre um botão que chame navigateTo(SupportingPaneScaffoldRole.Supporting) para mostrar o painel de suporte.

Confira uma implementação completa do esqueleto:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        AnimatedPane(
            modifier = Modifier
                .safeContentPadding()
                .background(Color.Red)
        ) {
            if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) {
                Button(
                    modifier = Modifier
                        .wrapContentSize(),
                    onClick = {
                        scope.launch {
                            scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting)
                        }
                    }
                ) {
                    Text("Show supporting pane")
                }
            } else {
                Text("Supporting pane is shown")
            }
        }
    },
    supportingPane = {
        AnimatedPane(modifier = Modifier.safeContentPadding()) {
            Text("Supporting pane")
        }
    }
)

Extrair elementos combináveis do painel

Extraia os painéis individuais de um SupportingPaneScaffold para os próprios combináveis para torná-los reutilizáveis e testáveis. Use ThreePaneScaffoldScope para acessar AnimatedPane se você quiser as animações padrão:

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.MainPane(
    shouldShowSupportingPaneButton: Boolean,
    onNavigateToSupportingPane: () -> Unit,
    modifier: Modifier = Modifier,
) {
    AnimatedPane(
        modifier = modifier.safeContentPadding()
    ) {
        // Main pane content
        if (shouldShowSupportingPaneButton) {
            Button(onClick = onNavigateToSupportingPane) {
                Text("Show supporting pane")
            }
        } else {
            Text("Supporting pane is shown")
        }
    }
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.SupportingPane(
    modifier: Modifier = Modifier,
) {
    AnimatedPane(modifier = modifier.safeContentPadding()) {
        // Supporting pane content
        Text("This is the supporting pane")
    }
}

Extrair as panes em elementos combináveis simplifica o uso do SupportingPaneScaffold (compare o exemplo a seguir com a implementação completa do scaffold na seção anterior):

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)

Se você precisar de mais controle sobre aspectos específicos do scaffold, use SupportingPaneScaffold em vez de NavigableSupportingPaneScaffold. Ele aceita um PaneScaffoldDirective e ThreePaneScaffoldValue ou ThreePaneScaffoldState separadamente. Essa flexibilidade permite implementar uma lógica personalizada para o espaçamento de painéis e determinar quantos painéis devem ser exibidos simultaneamente. Também é possível ativar o suporte de volta preditiva adicionando ThreePaneScaffoldPredictiveBackHandler.

Adicionar ThreePaneScaffoldPredictiveBackHandler

Anexe o gerenciador de volta preditiva que usa uma instância de navegador de esqueleto e especifique o backBehavior. Isso determina como os destinos são retirados da backstack durante a navegação de volta. Em seguida, transmita o scaffoldDirective e scaffoldState para SupportingPaneScaffold. Use a sobrecarga que aceita um ThreePaneScaffoldState, transmitindo scaffoldNavigator.scaffoldState.

Defina os painéis principais e de suporte em SupportingPaneScaffold. Use AnimatedPane para animações padrão do painel.

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

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

ThreePaneScaffoldPredictiveBackHandler(
    navigator = scaffoldNavigator,
    backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
)

SupportingPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    scaffoldState = scaffoldNavigator.scaffoldState,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)