Navigation für responsive Benutzeroberflächen

Als Navigation bezeichnet man die Interaktion mit der Benutzeroberfläche einer Anwendung, um auf die Inhaltsziele der Anwendung zuzugreifen. Die Prinzipien der Android-Navigation enthalten Richtlinien, die Ihnen dabei helfen, eine einheitliche, intuitive App-Navigation zu schaffen.

Responsive Benutzeroberflächen bieten responsive Inhalte und enthalten oft verschiedene Arten von Navigationselementen, wenn die Anzeigegröße geändert wird – zum Beispiel eine Navigationsleiste unten auf kleinen Displays, eine Navigationsleiste auf mittelgroßen Displays oder eine dauerhafte Navigationsleiste auf großen Displays. Responsive Benutzeroberflächen sollten jedoch weiterhin den Prinzipien der Navigation entsprechen.

Die Navigationskomponente von Jetpack implementiert die Prinzipien der Navigation und kann verwendet werden, um die Entwicklung von Apps mit responsiven Benutzeroberflächen zu erleichtern.

Abbildung 1: Maximierte, mittlere und kompakte Displays mit Navigationsleiste, Seitenleiste und unterer Leiste.

Responsive UI-Navigation

Die Größe des Displayfensters einer App wirkt sich auf die Ergonomie und die Nutzerfreundlichkeit aus. Mit Fenstergrößenklassen können Sie geeignete Navigationselemente (z. B. Navigationsleisten, Schienen oder Schubladen) bestimmen und diese dort platzieren, wo sie für den Nutzer am besten zugänglich sind. In den Layoutrichtlinien von Material Design nehmen Navigationselemente einen festen Bereich an der Vorderseite des Displays ein und können zum unteren Rand verschoben werden, wenn die App-Breite kompakt ist. Ihre Auswahl der Navigationselemente hängt hauptsächlich von der Größe des App-Fensters und der Anzahl der Elemente ab, die das Element enthalten muss.

Fenstergrößenklasse Einige Elemente Viele Elemente
Kompakte Breite untere Navigationsleiste Navigationsleiste (oberer oder unterer Rand)
mittlere Breite Navigationsschiene Navigationsleiste (oberer Rand)
Breite nach Expansion Navigationsschiene dauerhafte Navigationsleiste (oberer Rand)

In ansichtsbasierten Layouts können Layout-Ressourcendateien durch Haltepunkte in Fenstergrößenklassen bestimmt werden, um verschiedene Navigationselemente für verschiedene Anzeigeabmessungen zu verwenden. Jetpack Compose kann Haltepunkte verwenden, die von der Fenstergrößen-API bereitgestellt werden, um programmatisch das für das App-Fenster am besten geeignete Navigationselement zu bestimmen.

Aufrufe

<!-- res/layout/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>


<!-- res/layout-w600dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigationrail.NavigationRailView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>


<!-- res/layout-w1240dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigation.NavigationView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

Schreiben

// This method should be run inside a Composable function.
val widthSizeClass = calculateWindowSizeClass(this).widthSizeClass
// You can get the height of the current window by invoking heightSizeClass instead.

@Composable
fun MyApp(widthSizeClass: WindowWidthSizeClass) {
    // Select a navigation element based on window size.
    when (widthSizeClass) {
        WindowWidthSizeClass.Compact -> { CompactScreen() }
        WindowWidthSizeClass.Medium -> { MediumScreen() }
        WindowWidthSizeClass.Expanded -> { ExpandedScreen() }
    }
}

@Composable
fun CompactScreen() {
    Scaffold(bottomBar = {
                NavigationBar {
                    icons.forEach { item ->
                        NavigationBarItem(
                            selected = isSelected,
                            onClick = { ... },
                            icon = { ... })
                    }
                }
            }
        ) {
        // Other content
    }
}

@Composable
fun MediumScreen() {
    Row(modifier = Modifier.fillMaxSize()) {
        NavigationRail {
            icons.forEach { item ->
                NavigationRailItem(
                    selected = isSelected,
                    onClick = { ... },
                    icon = { ... })
            }
        }
        // Other content
    }
}

@Composable
fun ExpandedScreen() {
    PermanentNavigationDrawer(
        drawerContent = {
            icons.forEach { item ->
                NavigationDrawerItem(
                    icon = { ... },
                    label = { ... },
                    selected = isSelected,
                    onClick = { ... }
                )
            }
        },
        content = {
            // Other content
        }
    )
}

Ziele für responsive Inhalte

In einer responsiven Benutzeroberfläche muss sich das Layout jedes Inhaltsziels an Änderungen der Fenstergröße anpassen. Ihre App kann den Layoutabstand anpassen, Elemente neu positionieren, Inhalte hinzufügen oder entfernen sowie UI-Elemente einschließlich Navigationselemente ändern. Weitere Informationen finden Sie unter UI zu responsiven Layouts migrieren und Adaptive Layouts erstellen.

Wenn jedes einzelne Ziel Größenänderungsereignisse ordnungsgemäß verarbeitet, werden Änderungen auf der Benutzeroberfläche isoliert. Der Rest des App-Status, einschließlich der Navigation, ist davon nicht betroffen.

Die Navigation sollte nicht als Nebeneffekt von Änderungen der Fenstergröße auftreten. Erstellen Sie keine Inhaltsziele nur für unterschiedliche Fenstergrößen. So sollten beispielsweise für die verschiedenen Displays eines faltbaren Geräts keine unterschiedlichen Inhaltsziele erstellt werden.

Die Navigation als Nebeneffekt von Änderungen der Fenstergröße führt zu folgenden Problemen:

  • Das alte Ziel (für die vorherige Fenstergröße) ist möglicherweise kurzzeitig sichtbar, bevor das neue Ziel aufgerufen wird.
  • Um die Umkehrbarkeit aufrechtzuerhalten (z. B. beim Zuklappen und Aufklappen eines Geräts), ist die Navigation für jede Fenstergröße erforderlich.
  • Die Aufrechterhaltung des Anwendungsstatus zwischen Zielen kann schwierig sein, da durch die Navigation der Zustand beim Aufrufen des Backstacks zerstört werden kann

Außerdem ist Ihre App möglicherweise nicht im Vordergrund zu sehen, während die Fenstergröße geändert wird. Das Layout Ihrer App benötigt möglicherweise mehr Platz als die Vordergrund-App, und wenn der Nutzer zu Ihrer App zurückkehrt, könnten sich Ausrichtung und Fenstergröße verändert haben.

Wenn für Ihre App eindeutige Inhaltsziele basierend auf der Fenstergröße erforderlich sind, sollten Sie die relevanten Ziele in einer einzigen Zielanwendung mit alternativen Layouts kombinieren.

Inhaltsziele mit alternativen Layouts

Als Teil eines responsiven Designs kann ein einzelnes Navigationsziel je nach Größe des App-Fensters alternative Layouts haben. Jedes Layout nimmt das gesamte Fenster ein, aber es werden unterschiedliche Layouts für verschiedene Fenstergrößen dargestellt.

Ein kanonisches Beispiel ist die Listen-Detailansicht. Bei kleinen Fenstergrößen zeigt Ihre App ein Inhaltslayout für die Liste und eines für die Details an. Wenn Sie zum Ziel der Listen-Detailansicht navigieren, wird zunächst nur das Listenlayout angezeigt. Wenn ein Listenelement ausgewählt wird, wird in Ihrer App das Detaillayout angezeigt, wodurch die Liste ersetzt wird. Wenn das hintere Steuerelement ausgewählt ist, wird das Listenlayout angezeigt, wobei das Detail ersetzt wird. Bei erweiterten Fenstergrößen werden das Listen- und das Detaillayout jedoch nebeneinander angezeigt.

Aufrufe

Mit SlidingPaneLayout können Sie ein einzelnes Navigationsziel erstellen, bei dem auf großen Bildschirmen zwei Inhaltsbereiche nebeneinander angezeigt werden, auf Geräten mit kleinen Bildschirmen wie Smartphones jedoch jeweils nur ein Bereich.

<!-- Single destination for list and detail. -->

<navigation ...>

    <!-- Fragment that implements SlidingPaneLayout. -->
    <fragment
        android:id="@+id/article_two_pane"
        android:name="com.example.app.ListDetailTwoPaneFragment" />

    <!-- Other destinations... -->
</navigation>

Weitere Informationen zur Implementierung eines Layouts mit Listendetails mithilfe von SlidingPaneLayout finden Sie unter Layout mit zwei Bereichen erstellen.

Schreiben

In Compose kann eine Listen-Detailansicht implementiert werden, indem alternative zusammensetzbare Funktionen in einer einzigen Route kombiniert werden, die Fenstergrößenklassen verwendet, um die entsprechende zusammensetzbare Funktion für jede Größenklasse auszugeben.

Eine Route ist der Navigationspfad zu einem Inhaltsziel. Dabei handelt es sich in der Regel um eine einzelne zusammensetzbare Funktion, die jedoch auch alternative zusammensetzbare Funktionen sein kann. Die Geschäftslogik bestimmt, welche der alternativen zusammensetzbaren Funktionen angezeigt werden. Die zusammensetzbare Funktion füllt das App-Fenster aus, unabhängig davon, welche Alternative angezeigt wird.

Die Listen-Detailansicht besteht aus drei zusammensetzbaren Funktionen. Beispiel:

/* Displays a list of items. */
@Composable
fun ListOfItems(
    onItemSelected: (String) -> Unit,
) { /*...*/ }

/* Displays the detail for an item. */
@Composable
fun ItemDetail(
    selectedItemId: String? = null,
) { /*...*/ }

/* Displays a list and the detail for an item side by side. */
@Composable
fun ListAndDetail(
    selectedItemId: String? = null,
    onItemSelected: (String) -> Unit,
) {
  Row {
    ListOfItems(onItemSelected = onItemSelected)
    ItemDetail(selectedItemId = selectedItemId)
  }
}

Über eine einzelne Navigationsroute haben Sie Zugriff auf die Listen-Detailansicht:

@Composable
fun ListDetailRoute(
    // Indicates that the display size is represented by the expanded window size class.
    isExpandedWindowSize: Boolean = false,
    // Identifies the item selected from the list. If null, a item has not been selected.
    selectedItemId: String?,
) {
  if (isExpandedWindowSize) {
    ListAndDetail(
      selectedItemId = selectedItemId,
      /*...*/
    )
  } else {
    // If the display size cannot accommodate both the list and the item detail,
    // show one of them based on the user's focus.
    if (selectedItemId != null) {
      ItemDetail(
        selectedItemId = selectedItemId,
        /*...*/
      )
    } else {
      ListOfItems(/*...*/)
    }
  }
}

ListDetailRoute (das Navigationsziel) bestimmt, welche der drei zusammensetzbaren Funktionen ausgegeben werden soll: ListAndDetail für die maximierte Fenstergröße; ListOfItems oder ItemDetail für „Kompakt“, je nachdem, ob ein Listenelement ausgewählt wurde.

Die Route ist in einem NavHost enthalten. Beispiel:

NavHost(navController = navController, startDestination = "listDetailRoute") {
  composable("listDetailRoute") {
    ListDetailRoute(isExpandedWindowSize = isExpandedWindowSize,
                    selectedItemId = selectedItemId)
  }
  /*...*/
}

Sie können das Argument isExpandedWindowSize angeben, indem Sie WindowMetrics Ihrer Anwendung untersuchen.

Das Argument selectedItemId kann von einem ViewModel bereitgestellt werden, das den Status über alle Fenstergrößen hinweg beibehält. Wenn der Nutzer ein Element aus der Liste auswählt, wird die Statusvariable selectedItemId aktualisiert:

class ListDetailViewModel : ViewModel() {

  data class ListDetailUiState(
      val selectedItemId: String? = null,
  )

  private val viewModelState = MutableStateFlow(ListDetailUiState())

  fun onItemSelected(itemId: String) {
    viewModelState.update {
      it.copy(selectedItemId = itemId)
    }
  }
}

val listDetailViewModel = ListDetailViewModel()

@Composable
fun ListDetailRoute(
    isExpandedWindowSize: Boolean = false,
    selectedItemId: String?,
    onItemSelected: (String) -> Unit = { listDetailViewModel.onItemSelected(it) },
) {
  if (isExpandedWindowSize) {
    ListAndDetail(
      selectedItemId = selectedItemId,
      onItemSelected = onItemSelected,
      /*...*/
    )
  } else {
    if (selectedItemId != null) {
      ItemDetail(
        selectedItemId = selectedItemId,
        /*...*/
      )
    } else {
      ListOfItems(
        onItemSelected = onItemSelected,
        /*...*/
      )
    }
  }
}

Die Route enthält auch ein benutzerdefiniertes BackHandler, wenn die zusammensetzbare Funktion der Elementdetails das gesamte App-Fenster einnimmt:

class ListDetailViewModel : ViewModel() {

  data class ListDetailUiState(
      val selectedItemId: String? = null,
  )

  private val viewModelState = MutableStateFlow(ListDetailUiState())

  fun onItemSelected(itemId: String) {
    viewModelState.update {
      it.copy(selectedItemId = itemId)
    }
  }

  fun onItemBackPress() {
    viewModelState.update {
      it.copy(selectedItemId = null)
    }
  }
}

val listDetailViewModel = ListDetailViewModel()

@Composable
fun ListDetailRoute(
    isExpandedWindowSize: Boolean = false,
    selectedItemId: String?,
    onItemSelected: (String) -> Unit = { listDetailViewModel.onItemSelected(it) },
    onItemBackPress: () -> Unit = { listDetailViewModel.onItemBackPress() },
) {
  if (isExpandedWindowSize) {
    ListAndDetail(
      selectedItemId = selectedItemId,
      onItemSelected = onItemSelected,
      /*...*/
    )
  } else {
    if (selectedItemId != null) {
      ItemDetail(
        selectedItemId = selectedItemId,
        /*...*/
      )
      BackHandler {
        onItemBackPress()
      }
    } else {
      ListOfItems(
        onItemSelected = onItemSelected,
        /*...*/
      )
    }
  }
}

Die Kombination des App-Status aus einem ViewModel mit Informationen zur Fenstergröße macht die Auswahl der passenden zusammensetzbaren Funktion eine Sache der einfachen Logik. Durch die Aufrechterhaltung eines unidirektionalen Datenflusses kann Ihre App den verfügbaren Platz voll nutzen und gleichzeitig den Anwendungsstatus beibehalten.

Eine vollständige Implementierung der Listen-Detailansicht in Compose finden Sie im JetNews-Beispiel auf GitHub.

Ein Navigationsdiagramm

Um eine einheitliche User Experience auf jedem Gerät oder jeder Fenstergröße zu bieten, sollten Sie ein einzelnes Navigationsdiagramm verwenden, bei dem das Layout jedes Inhaltsziels responsiv ist.

Wenn Sie für jede Fenstergrößenklasse ein anderes Navigationsdiagramm verwenden, müssen Sie bei jedem Wechsel der App von einer Größenklasse zu einer anderen das aktuelle Ziel des Nutzers in den anderen Grafiken ermitteln, einen Back Stack erstellen und Statusinformationen abgleichen, die zwischen den Diagrammen unterschiedlich sind.

Host für verschachtelte Navigation

Ihre App kann ein Ziel für Inhalte enthalten, für das eigene Ziele festgelegt sind. In einer Listen-Detailansicht könnte der Detailbereich des Artikels beispielsweise UI-Elemente enthalten, die zu Inhalten navigieren, die das Objektdetail ersetzen.

Um diese Art von Subnavigation zu implementieren, kann der Detailbereich ein verschachtelter Navigationshost mit einem eigenen Navigationsdiagramm sein, das die Ziele angibt, auf die über den Detailbereich zugegriffen wird:

Aufrufe

<!-- layout/two_pane_fragment.xml -->

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list_pane"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"/>

    <!-- Detail pane is a nested navigation host. Its graph is not connected
         to the main graph that contains the two_pane_fragment destination. -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detail_pane"
        android:layout_width="300dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/detail_pane_nav_graph" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

Schreiben

@Composable
fun ItemDetail(selectedItemId: String? = null) {
    val navController = rememberNavController()
    NavHost(navController, "itemSubdetail1") {
        composable("itemSubdetail1") { ItemSubdetail1(...) }
        composable("itemSubdetail2") { ItemSubdetail2(...) }
        composable("itemSubdetail3") { ItemSubdetail3(...) }
    }
}

Diese Grafik unterscheidet sich von einer verschachtelten Navigationsgrafik, da die Navigationsgrafik der verschachtelten NavHost nicht mit der Hauptnavigationsgrafik verbunden ist. Das heißt, Sie können nicht direkt von Zielen in einem Diagramm zu Zielen im anderen navigieren.

Weitere Informationen finden Sie unter Verschachtelte Navigationsgrafiken und Mit der Funktion „Compose“ navigieren.

Beibehaltener Status

Damit Ziele für responsive Inhalte bereitgestellt werden können, muss Ihre App ihren Zustand beibehalten, wenn das Gerät gedreht oder zugeklappt wird oder die Größe des App-Fensters geändert wird. Standardmäßig werden durch solche Konfigurationsänderungen die Aktivitäten, Fragmente, die Ansichtshierarchie und die zusammensetzbaren Funktionen der App neu erstellt. Die empfohlene Methode zum Speichern des UI-Status ist ein ViewModel oder rememberSaveable, das auch nach Konfigurationsänderungen bestehen bleibt. Weitere Informationen finden Sie unter UI-Status speichern und State und Jetpack Compose.

Größenänderungen sollten rückgängig gemacht werden können, z. B. wenn der Nutzer das Gerät dreht und es dann wieder zurückdreht.

Responsive Layouts können verschiedene Inhalte in verschiedenen Fenstergrößen anzeigen. Daher müssen responsive Layouts häufig einen zusätzlichen inhaltlichen Zustand speichern, auch wenn der Zustand nicht auf die aktuelle Fenstergröße zutrifft. Beispielsweise kann ein Layout Platz haben, um nur bei größeren Fensterbreiten ein zusätzliches Scroll-Widget anzuzeigen. Wenn ein Größenänderungsereignis dazu führt, dass die Fensterbreite zu klein wird, wird das Widget ausgeblendet. Wenn die App die Größe auf ihre vorherigen Abmessungen anpasst, wird das Scroll-Widget wieder sichtbar und die ursprüngliche Scroll-Position sollte wiederhergestellt werden.

ViewModel-Bereiche

Im Entwicklerleitfaden für die Migration zur Navigationskomponente wird eine Architektur mit einer einzigen Aktivität empfohlen, bei der Ziele als Fragmente und ihre Datenmodelle mit ViewModel implementiert werden.

Eine ViewModel ist immer auf einen Lebenszyklus beschränkt. Sobald dieser Lebenszyklus endgültig endet, wird die ViewModel gelöscht und kann verworfen werden. Der Lebenszyklus, dem die ViewModel zugeordnet ist – und wie breit die ViewModel freigegeben werden kann – hängt davon ab, welcher Property-Delegaten verwendet wird, um die ViewModel abzurufen.

Im einfachsten Fall ist jedes Navigationsziel ein einzelnes Fragment mit einem vollständig isolierten UI-Status. Daher kann jedes Fragment den viewModels()-Property-Delegaten verwenden, um ein für dieses Fragment bezogenes ViewModel abzurufen.

Um den UI-Status zwischen Fragmenten zu teilen, definieren Sie den ViewModel auf die Aktivität, indem Sie activityViewModels() in den Fragmenten aufrufen (das Äquivalent für die Aktivität ist nur viewModels()). Dadurch können die Aktivität und alle daran angehängten Fragmente die Instanz ViewModel gemeinsam nutzen. In einer Architektur mit einer einzelnen Aktivität bleibt der ViewModel-Bereich jedoch so lange effektiv wie die App, sodass der ViewModel im Arbeitsspeicher bleibt, auch wenn er nicht von Fragmenten verwendet wird.

Angenommen, Ihre Navigationsgrafik enthält eine Abfolge von Fragmentzielen, die einen Bezahlvorgang darstellen, und der aktuelle Status für den gesamten Bezahlvorgang befindet sich in einem ViewModel, das von den Fragmenten gemeinsam genutzt wird. Der Umfang von ViewModel auf die Aktivität ist nicht nur zu breit gefasst, sondern wirft auch ein weiteres Problem auf: Wenn der Nutzer den Bezahlvorgang für eine Bestellung durchläuft und diesen dann noch einmal für eine zweite Bestellung durchläuft, verwenden beide Bestellungen dieselbe Instanz der Zahlungs-ViewModel. Vor der zweiten Bestellung müssen Sie die Daten aus der ersten Bestellung manuell löschen. Fehler können für den Nutzer teuer werden.

Legen Sie stattdessen für den ViewModel ein Navigationsdiagramm im aktuellen NavController fest. Erstellen Sie ein verschachteltes Navigationsdiagramm, das die Ziele enthält, die Teil des Bezahlvorgangs sind. Verwenden Sie dann in jedem dieser Fragmentziele den Property-Delegaten navGraphViewModels() und übergeben Sie die ID des Navigationsdiagramms, um die freigegebene ViewModel zu erhalten. Dadurch wird sichergestellt, dass die entsprechende Instanz von ViewModel verworfen und nicht für den nächsten Bezahlvorgang verwendet wird, sobald der Nutzer den Bezahlvorgang beendet und die verschachtelte Navigationsgrafik nicht mehr berücksichtigt wird.

Aufgabenstellung Property-Bevollmächtigte Kann ViewModel freigeben für
Fragment Fragment.viewModels() Nur aktuelles Fragment
Aktivitäten Activity.viewModels()

Fragment.activityViewModels()

Aktivität und alle daran angehängten Fragmente
Navigationsdiagramm Fragment.navGraphViewModels() Alle Fragmente in derselben Navigationsgrafik

Wenn Sie einen verschachtelten Navigationshost (siehe oben) verwenden, können Ziele in diesem Host keine ViewModel-Werte mit Zielen außerhalb des Hosts teilen, wenn navGraphViewModels() verwendet wird, da die Diagramme nicht verbunden sind. In diesem Fall können Sie stattdessen den Umfang der Aktivität verwenden.

Hochgefahrener Zustand

In „Compose“ können Sie den Zustand beibehalten, wenn die Fenstergröße durch Heizen des Status geändert wird. Durch das Hochziehen des Status von zusammensetzbaren Funktionen an eine höhere Position in der Zusammensetzungsstruktur kann der Zustand beibehalten werden, auch wenn die zusammensetzbaren Funktionen nicht mehr sichtbar sind.

Im Abschnitt Erstellen von Inhaltsziele mit alternativen Layouts oben haben wir den Status der zusammensetzbaren Funktionen der Listen-Detailansicht nach ListDetailRoute hochgezogen, sodass dieser Status unabhängig davon erhalten bleibt, welche zusammensetzbare Funktion angezeigt wird:

@Composable
fun ListDetailRoute(
    // Indicates that the display size is represented by the expanded window size class.
    isExpandedWindowSize: Boolean = false,
    // Identifies the item selected from the list. If null, a item has not been selected.
    selectedItemId: String?,
) { /*...*/ }

Weitere Informationen