Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.compose.snippets.navigation3.migration

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.StateFlow
import androidx.compose.runtime.mutableStateOf
import androidx.navigation3.runtime.EntryProviderScope
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.scene.DialogSceneStrategy
import androidx.navigation3.ui.NavDisplay
import kotlinx.serialization.Serializable

// Dummy screens for compilation
@Composable private fun ScreenA(title: String) {}
@Composable private fun ScreenB() {}
@Composable private fun ScreenD() {}

private object Snippet4 {
// [START android_compose_navigation3_route_a_after]
@Serializable data object RouteA : NavKey
// [END android_compose_navigation3_route_a_after]
}

private object Snippet7 {
@Serializable data object RouteA : NavKey

@Composable
fun SetupSnippet() {
// [START android_compose_navigation3_remember_navigation_state]
val navigationState = rememberNavigationState(
// [START_EXCLUDE]
startRoute = RouteA,
topLevelRoutes = setOf(RouteA)
/*
// [END_EXCLUDE]
startRoute = <Insert your starting route>,
topLevelRoutes = <Insert your set of top level routes>
// [START_EXCLUDE]
*/
// [END_EXCLUDE]
)

val navigator = remember { Navigator(navigationState) }
// [END android_compose_navigation3_remember_navigation_state]
}
}

private object Snippet9 {
@Serializable data object RouteA : NavKey

fun IsSelectedAfterSnippet(navigationState: NavigationState) {
val key = RouteA
// [START android_compose_navigation3_is_selected_after]
val isSelected = key == navigationState.topLevelRoute
// [END android_compose_navigation3_is_selected_after]
}
}

private object Snippet10 {
fun EntryProviderSnippet() {
// [START android_compose_navigation3_entry_provider]
val entryProvider = entryProvider<NavKey> {

}
// [END android_compose_navigation3_entry_provider]
}
}

private object Snippet12 {
// [START android_compose_navigation3_nav_host_after]
// [START_EXCLUDE]
/*
// [END_EXCLUDE]
import androidx.navigation3.runtime.EntryProviderScope
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.scene.DialogSceneStrategy
// [START_EXCLUDE]
*/
// [END_EXCLUDE]

@Serializable data class RouteA(val id: String) : NavKey
@Serializable data object RouteB : NavKey
@Serializable data object RouteD : NavKey

val entryProvider = entryProvider {
entry<RouteA>{ key -> ScreenA(title = "Screen has ID: ${key.id}") }
featureBSection()
entry<RouteD>(metadata = DialogSceneStrategy.dialog()){ ScreenD() }
}

fun EntryProviderScope<NavKey>.featureBSection() {
entry<RouteB> { ScreenB() }
}
// [END android_compose_navigation3_nav_host_after]
}

private object Snippet13 {
@Serializable data object RouteA : NavKey
val entryProvider = entryProvider<NavKey> { }
val navigationState = NavigationState(RouteA, mutableStateOf(RouteA), emptyMap())
val navigator = Navigator(navigationState)

@Composable
fun NavDisplaySnippet() {
// [START android_compose_navigation3_nav_display]
NavDisplay(
entries = navigationState.toEntries(entryProvider),
onBack = { navigator.goBack() },
sceneStrategies = remember { listOf(DialogSceneStrategy()) }
)
// [END android_compose_navigation3_nav_display]
}
}

private object SnippetLifecycleAfter {
@Composable
fun LifecycleSnippet(flow: StateFlow<String>) {
// [START android_compose_navigation3_lifecycle_after]
// Inside the destination composable
val state by flow.collectAsStateWithLifecycle()
// [END android_compose_navigation3_lifecycle_after]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.compose.snippets.navigation3.migration

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import kotlinx.coroutines.flow.StateFlow
import androidx.navigation.NavHostController
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.dialog
import androidx.navigation.compose.navigation
import androidx.navigation.toRoute
import kotlinx.serialization.Serializable
import kotlin.reflect.KClass

// Dummy screens for compilation
@Composable private fun ScreenA(title: String) {}
@Composable private fun ScreenB() {}
@Composable private fun ScreenD() {}

private object Snippet3 {
// [START android_compose_navigation3_route_a_before]
@Serializable data object RouteA
// [END android_compose_navigation3_route_a_before]
}

private object Snippet8 {
@Serializable data object RouteA

// [START android_compose_navigation3_is_selected_before]
// [START_EXCLUDE]
@Composable
fun Dummy(navController: NavController) {
val key = RouteA
// [END_EXCLUDE]
val isSelected = navController.currentBackStackEntryAsState().value?.destination.isRouteInHierarchy(key::class)
// [START_EXCLUDE]
}
// [END_EXCLUDE]

fun NavDestination?.isRouteInHierarchy(route: KClass<*>) =
this?.hierarchy?.any {
it.hasRoute(route)
} ?: false
// [END android_compose_navigation3_is_selected_before]
}

private object Snippet11 {
// [START android_compose_navigation3_nav_host_before]
// [START_EXCLUDE]
/*
// [END_EXCLUDE]
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hasRoute
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.dialog
import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import androidx.navigation.toRoute
// [START_EXCLUDE]
*/
// [END_EXCLUDE]

@Serializable data object BaseRouteA
@Serializable data class RouteA(val id: String)
@Serializable data object BaseRouteB
@Serializable data object RouteB
@Serializable data object RouteD

@Composable
fun NavHostSnippet(navController: NavHostController) {
NavHost(navController = navController, startDestination = BaseRouteA){
composable<RouteA>{ entry ->
val id = entry.toRoute<RouteA>().id
ScreenA(title = "Screen has ID: $id")
}
featureBSection()
dialog<RouteD>{ ScreenD() }
}
}

fun NavGraphBuilder.featureBSection() {
navigation<BaseRouteB>(startDestination = RouteB) {
composable<RouteB> { ScreenB() }
}
}
// [END android_compose_navigation3_nav_host_before]
}

private object SnippetLifecycleBefore {
@Composable
fun LifecycleSnippet(navController: NavController, flow: StateFlow<String>) {
// [START android_compose_navigation3_lifecycle_before]
// In your destination screen or host
val lifecycleOwner = navController.currentBackStackEntry!!
val state by flow.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)
// [END android_compose_navigation3_lifecycle_before]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.compose.snippets.navigation3.migration

// [START android_compose_navigation3_navigation_state]
// package com.example.project

import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSerializable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.toMutableStateList
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.rememberDecoratedNavEntries
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.runtime.serialization.NavKeySerializer
import androidx.savedstate.compose.serialization.serializers.MutableStateSerializer

/**
* Create a navigation state that persists config changes and process death.
*/
@Composable
fun rememberNavigationState(
startRoute: NavKey,
topLevelRoutes: Set<NavKey>
): NavigationState {

val topLevelRoute = rememberSerializable(
startRoute, topLevelRoutes,
serializer = MutableStateSerializer(NavKeySerializer())
) {
mutableStateOf(startRoute)
}

val backStacks = topLevelRoutes.associateWith { key -> rememberNavBackStack(key) }

return remember(startRoute, topLevelRoutes) {
NavigationState(
startRoute = startRoute,
topLevelRoute = topLevelRoute,
backStacks = backStacks
)
}
}

/**
* State holder for navigation state.
*
* @param startRoute - the start route. The user will exit the app through this route.
* @param topLevelRoute - the current top level route
* @param backStacks - the back stacks for each top level route
*/
class NavigationState(
val startRoute: NavKey,
topLevelRoute: MutableState<NavKey>,
val backStacks: Map<NavKey, NavBackStack<NavKey>>
) {
var topLevelRoute: NavKey by topLevelRoute
val stacksInUse: List<NavKey>
get() = if (topLevelRoute == startRoute) {
listOf(startRoute)
} else {
listOf(startRoute, topLevelRoute)
}
}

/**
* Convert NavigationState into NavEntries.
*/
@Composable
fun NavigationState.toEntries(
entryProvider: (NavKey) -> NavEntry<NavKey>
): SnapshotStateList<NavEntry<NavKey>> {

val decoratedEntries = backStacks.mapValues { (_, stack) ->
val decorators = listOf(
rememberSaveableStateHolderNavEntryDecorator<NavKey>(),
)
rememberDecoratedNavEntries(
backStack = stack,
entryDecorators = decorators,
entryProvider = entryProvider
)
}

return stacksInUse
.flatMap { decoratedEntries[it] ?: emptyList() }
.toMutableStateList()
}
// [END android_compose_navigation3_navigation_state]
Loading
Loading