Crea un layout per i riquadri di supporto

Il layout del riquadro di supporto mantiene l'attenzione dell'utente sui contenuti principali dell'app mentre vengono visualizzate informazioni di supporto pertinenti. Ad esempio, il riquadro principale potrebbe mostrare i dettagli di un film, mentre il riquadro secondario elenca film simili, film dello stesso regista o opere con gli stessi attori.

Per maggiori dettagli, consulta le linee guida per il riquadro di supporto di Material 3.

Implementa un riquadro di supporto con una struttura

NavigableSupportingPaneScaffold è un composable che semplifica l'implementazione di un layout del riquadro di supporto in Jetpack Compose. Esegue il wrapping di SupportingPaneScaffold e aggiunge la navigazione integrata e la gestione della cronologia predittiva.

Un'impalcatura del riquadro di supporto supporta fino a tre riquadri:

  • Riquadro principale: mostra i contenuti principali.
  • Riquadro di supporto: fornisce contesto o strumenti aggiuntivi correlati al riquadro principale.
  • Riquadro aggiuntivo (facoltativo): utilizzato per i contenuti supplementari, se necessario.

La struttura si adatta in base alle dimensioni della finestra:

  • Nelle finestre grandi, i riquadri principale e secondario vengono visualizzati uno accanto all'altro.
  • Nelle finestre piccole, è visibile un solo riquadro alla volta, che cambia man mano che gli utenti navigano.

    Contenuti principali che occupano la maggior parte del display con contenuti di supporto accanto.
    Figura 1. Supporto del layout del riquadro.

Aggiungi dipendenze

NavigableSupportingPaneScaffold fa parte della libreria di layout adattivi di Material 3.

Aggiungi le seguenti tre dipendenze correlate al file build.gradle della tua app o del tuo modulo:

Kotlin

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

Trendy

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • adattivi: componenti di base di basso livello come HingeInfo e Posture

  • adaptive-layout: layout adattivi come ListDetailPaneScaffold e SupportingPaneScaffold

  • adaptive-navigation: composable per navigare all'interno e tra i riquadri, nonché layout adattivi che supportano la navigazione per impostazione predefinita, come NavigableListDetailPaneScaffold e NavigableSupportingPaneScaffold

Assicurati che il progetto includa compose-material3-adaptive versione 1.1.0-beta1 o successive.

Attivare il gesto Indietro predittivo

Per attivare le animazioni del gesto Indietro predittivo in Android 15 o versioni precedenti, devi attivare il supporto del gesto Indietro predittivo. Per attivare la funzionalità, aggiungi android:enableOnBackInvokedCallback="true" al tag <application> o ai singoli tag <activity> all'interno del file AndroidManifest.xml.

Una volta che la tua app ha come target Android 16 (livello API 36) o versioni successive, la navigazione predittiva è attivata per impostazione predefinita.

Crea un navigatore

Nelle finestre piccole, viene visualizzato un solo riquadro alla volta, quindi utilizza un ThreePaneScaffoldNavigator per spostarti tra i riquadri. Crea un'istanza del navigatore con rememberSupportingPaneScaffoldNavigator.

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

Passa il navigatore allo scaffold

Lo scaffold richiede un ThreePaneScaffoldNavigator, ovvero un'interfaccia che rappresenta lo stato dello scaffold, ThreePaneScaffoldValue e un PaneScaffoldDirective.

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

Il riquadro principale e quello di supporto sono composable che contengono i tuoi contenuti. Utilizza AnimatedPane per applicare le animazioni predefinite del riquadro durante la navigazione. Utilizza il valore dello scaffold per verificare se il riquadro di supporto è nascosto. In caso affermativo, visualizza un pulsante che chiama navigateTo(SupportingPaneScaffoldRole.Supporting) per visualizzare il riquadro di supporto.

Ecco un'implementazione completa dello scaffold:

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")
        }
    }
)

Componenti componibili del riquadro Estrai

Estrai i singoli riquadri di un SupportingPaneScaffold nei propri componenti componibili per renderli riutilizzabili e testabili. Utilizza ThreePaneScaffoldScope per accedere a AnimatedPane se vuoi le animazioni predefinite:

@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")
    }
}

L'estrazione dei riquadri in componibili semplifica l'utilizzo di SupportingPaneScaffold (confronta quanto segue con l'implementazione completa dello scaffold nella sezione precedente):

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 hai bisogno di un maggiore controllo su aspetti specifici della struttura, valuta l'utilizzo di SupportingPaneScaffold anziché di NavigableSupportingPaneScaffold. Questo accetta PaneScaffoldDirective e ThreePaneScaffoldValue o ThreePaneScaffoldState separatamente. Questa flessibilità ti consente di implementare una logica personalizzata per la spaziatura dei riquadri e determinare quanti riquadri devono essere visualizzati contemporaneamente. Puoi anche attivare il supporto della navigazione predittiva aggiungendo ThreePaneScaffoldPredictiveBackHandler.

Aggiungi ThreePaneScaffoldPredictiveBackHandler

Collega il gestore del gesto Indietro predittivo che accetta un'istanza di Scaffold Navigator e specifica backBehavior. Determina come le destinazioni vengono rimosse dallo stack precedente durante la navigazione indietro. Poi passa scaffoldDirective e scaffoldState a SupportingPaneScaffold. Utilizza l'overload che accetta un ThreePaneScaffoldState, passando scaffoldNavigator.scaffoldState.

Definisci i riquadri principale e secondario all'interno di SupportingPaneScaffold. Utilizza AnimatedPane per le animazioni predefinite del riquadro.

Dopo aver implementato questi passaggi, il codice dovrebbe essere simile al seguente:

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() },
)