Skip to content

Commit b377cec

Browse files
Update Tutorial3_2BottomNavigationNestedNavigation.kt
1 parent 210c382 commit b377cec

1 file changed

Lines changed: 124 additions & 65 deletions

File tree

Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/Tutorial3_2BottomNavigationNestedNavigation.kt

Lines changed: 124 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
package com.smarttoolfactory.tutorial3_1navigation
44

55
import android.annotation.SuppressLint
6+
import android.os.Bundle
7+
import androidx.activity.compose.BackHandler
8+
import androidx.collection.forEach
69
import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
710
import androidx.compose.animation.core.tween
811
import androidx.compose.foundation.background
12+
import androidx.compose.foundation.border
913
import androidx.compose.foundation.layout.Arrangement
1014
import androidx.compose.foundation.layout.Column
1115
import androidx.compose.foundation.layout.PaddingValues
@@ -15,6 +19,8 @@ import androidx.compose.foundation.layout.height
1519
import androidx.compose.foundation.layout.padding
1620
import androidx.compose.foundation.lazy.LazyColumn
1721
import androidx.compose.foundation.lazy.items
22+
import androidx.compose.foundation.pager.HorizontalPager
23+
import androidx.compose.foundation.pager.rememberPagerState
1824
import androidx.compose.foundation.shape.RoundedCornerShape
1925
import androidx.compose.material3.Button
2026
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -27,9 +33,12 @@ import androidx.compose.material3.Text
2733
import androidx.compose.material3.TopAppBar
2834
import androidx.compose.material3.TopAppBarDefaults
2935
import androidx.compose.runtime.Composable
36+
import androidx.compose.runtime.LaunchedEffect
3037
import androidx.compose.runtime.collectAsState
3138
import androidx.compose.runtime.getValue
3239
import androidx.compose.runtime.mutableIntStateOf
40+
import androidx.compose.runtime.mutableStateListOf
41+
import androidx.compose.runtime.mutableStateOf
3342
import androidx.compose.runtime.remember
3443
import androidx.compose.runtime.saveable.rememberSaveable
3544
import androidx.compose.runtime.setValue
@@ -46,15 +55,19 @@ import androidx.navigation.NavController
4655
import androidx.navigation.NavDestination
4756
import androidx.navigation.NavDestination.Companion.hasRoute
4857
import androidx.navigation.NavDestination.Companion.hierarchy
58+
import androidx.navigation.NavGraph
59+
import androidx.navigation.NavGraph.Companion.findStartDestination
4960
import androidx.navigation.NavGraphBuilder
5061
import androidx.navigation.NavHostController
5162
import androidx.navigation.compose.NavHost
5263
import androidx.navigation.compose.composable
5364
import androidx.navigation.compose.currentBackStackEntryAsState
5465
import androidx.navigation.compose.navigation
5566
import androidx.navigation.compose.rememberNavController
67+
import androidx.navigation.get
5668
import androidx.navigation.toRoute
5769

70+
@SuppressLint("RestrictedApi")
5871
@Preview
5972
@Composable
6073
fun Tutorial3_2Screen() {
@@ -106,12 +119,13 @@ fun Tutorial3_2Screen() {
106119
}
107120
}
108121

122+
@SuppressLint("RestrictedApi")
109123
@Composable
110124
private fun MainContainer(
111-
onScreenClick: (
125+
onGoToProfileScreen: (
112126
route: Any,
113127
navBackStackEntry: NavBackStackEntry,
114-
) -> Unit,
128+
) -> Unit
115129
) {
116130
val items = remember {
117131
bottomRouteDataList()
@@ -141,11 +155,9 @@ private fun MainContainer(
141155
tonalElevation = 4.dp
142156
) {
143157
items.forEach { item: BottomRouteData ->
144-
145158
// Checks destination's route with type safety
146159
val selected =
147160
currentDestination?.hierarchy?.any { it.hasRoute(item.route::class) } == true
148-
149161
NavigationBarItem(
150162
selected = selected,
151163
icon = {
@@ -155,38 +167,19 @@ private fun MainContainer(
155167
)
156168
},
157169
onClick = {
158-
// Returns current destinations by parent-child relationship
159-
currentDestination?.hierarchy?.forEach { destination: NavDestination ->
160-
println("HIERARCHY: destination: $destination")
161-
}
170+
nestedNavController.navigate(route = item.route) {
171+
launchSingleTop = true
162172

163-
// This is for not opening same screen if current destination
164-
// is equal to target destination
165-
if (selected.not()) {
166-
167-
nestedNavController.navigate(route = item.route) {
168-
launchSingleTop = true
169-
170-
// 🔥 If restoreState = true and saveState = true are commented
171-
// routes other than Home1 are not saved
172-
restoreState = true
173-
174-
// Pop up backstack to the first destination and save state.
175-
// This makes going back
176-
// to the start destination when pressing back in any other bottom tab.
177-
popUpTo(findStartDestination(nestedNavController.graph).id) {
178-
saveState = true
179-
}
180-
181-
val startDestinationRoute =
182-
nestedNavController.graph.startDestinationRoute
183-
val startDestinationRecursive =
184-
findStartDestination(nestedNavController.graph).route
185-
println(
186-
"🔥 startDestinationRoute: $startDestinationRoute, " +
187-
"startDestinationRecursive: $startDestinationRecursive\n" +
188-
"navigating target route: ${item.route}"
189-
)
173+
// 🔥 If restoreState = true and saveState = true are commented
174+
// routes other than Home1 are not saved
175+
restoreState = true
176+
177+
178+
// Pop up backstack to the first destination and save state.
179+
// This makes going back
180+
// to the start destination when pressing back in any other bottom tab.
181+
popUpTo(findStartDestination(nestedNavController.graph).id) {
182+
saveState = true
190183
}
191184
}
192185
}
@@ -198,28 +191,39 @@ private fun MainContainer(
198191
NavHost(
199192
modifier = Modifier.padding(paddingValues),
200193
navController = nestedNavController,
201-
startDestination = BottomNavigationRoute.HomeRoute
194+
startDestination = BottomNavigationRoute.HomeGraph
202195
) {
203-
addBottomNavigationGraph(nestedNavController) { route, navBackStackEntry ->
204-
onScreenClick(route, navBackStackEntry)
205-
}
196+
addBottomNavigationGraph(
197+
nestedNavController = nestedNavController,
198+
onGoToProfileScreen = { route, navBackStackEntry ->
199+
onGoToProfileScreen(route, navBackStackEntry)
200+
},
201+
onBottomScreenClick = { route, navBackStackEntry ->
202+
nestedNavController.navigate(route)
203+
}
204+
)
206205
}
207206
}
208207
}
209208

209+
/**
210+
* @param onGoToProfileScreen lambda for navigating Profile screen from current screen with top NavHostController
211+
* @param onBottomScreenClick lambda for navigating with [nestedNavController] in BottomNavigation
212+
*/
210213
private fun NavGraphBuilder.addBottomNavigationGraph(
211214
nestedNavController: NavHostController,
212-
onScreenClick: (route: Any, navBackStackEntry: NavBackStackEntry) -> Unit,
215+
onGoToProfileScreen: (route: Any, navBackStackEntry: NavBackStackEntry) -> Unit,
216+
onBottomScreenClick: (route: Any, navBackStackEntry: NavBackStackEntry) -> Unit,
213217
) {
214-
navigation<BottomNavigationRoute.HomeRoute>(
218+
navigation<BottomNavigationRoute.HomeGraph>(
215219
startDestination = BottomNavigationRoute.HomeRoute1
216220
) {
217221
composable<BottomNavigationRoute.HomeRoute1> { from: NavBackStackEntry ->
218222
Screen(
219223
text = "Home Screen1",
220224
navController = nestedNavController,
221225
onClick = {
222-
nestedNavController.navigate(BottomNavigationRoute.HomeRoute2)
226+
onBottomScreenClick(BottomNavigationRoute.HomeRoute2, from)
223227
}
224228
)
225229
}
@@ -229,7 +233,7 @@ private fun NavGraphBuilder.addBottomNavigationGraph(
229233
text = "Home Screen2",
230234
navController = nestedNavController,
231235
onClick = {
232-
nestedNavController.navigate(BottomNavigationRoute.HomeRoute3)
236+
onBottomScreenClick(BottomNavigationRoute.HomeRoute3, from)
233237
}
234238
)
235239
}
@@ -242,15 +246,15 @@ private fun NavGraphBuilder.addBottomNavigationGraph(
242246
}
243247
}
244248

245-
navigation<BottomNavigationRoute.SettingsRoute>(
249+
navigation<BottomNavigationRoute.SettingsGraph>(
246250
startDestination = BottomNavigationRoute.SettingsRoute1
247251
) {
248252
composable<BottomNavigationRoute.SettingsRoute1> { from: NavBackStackEntry ->
249253
Screen(
250254
text = "Settings Screen",
251255
navController = nestedNavController,
252256
onClick = {
253-
nestedNavController.navigate(BottomNavigationRoute.SettingsRoute2)
257+
onBottomScreenClick(BottomNavigationRoute.SettingsRoute2, from)
254258
}
255259
)
256260
}
@@ -260,7 +264,7 @@ private fun NavGraphBuilder.addBottomNavigationGraph(
260264
text = "Settings Screen2",
261265
navController = nestedNavController,
262266
onClick = {
263-
nestedNavController.navigate(BottomNavigationRoute.SettingsRoute3)
267+
onBottomScreenClick(BottomNavigationRoute.SettingsRoute3, from)
264268
}
265269
)
266270
}
@@ -278,7 +282,7 @@ private fun NavGraphBuilder.addBottomNavigationGraph(
278282
text = "Favorites Screen",
279283
navController = nestedNavController,
280284
onClick = {
281-
onScreenClick(
285+
onGoToProfileScreen(
282286
Profile("Favorites"),
283287
from
284288
)
@@ -291,7 +295,7 @@ private fun NavGraphBuilder.addBottomNavigationGraph(
291295
text = "Notifications Screen",
292296
navController = nestedNavController,
293297
onClick = {
294-
onScreenClick(
298+
onGoToProfileScreen(
295299
Profile("Notifications"),
296300
from
297301
)
@@ -302,7 +306,7 @@ private fun NavGraphBuilder.addBottomNavigationGraph(
302306

303307
@SuppressLint("RestrictedApi")
304308
@Composable
305-
private fun Screen(
309+
fun Screen(
306310
text: String,
307311
navController: NavController,
308312
onClick: (() -> Unit)? = null,
@@ -349,28 +353,83 @@ private fun Screen(
349353

350354
val currentBackStack: List<NavBackStackEntry> by navController.currentBackStack.collectAsState()
351355

352-
LazyColumn(
353-
modifier = Modifier.fillMaxSize(),
354-
verticalArrangement = Arrangement.spacedBy(8.dp)
355-
) {
356+
val pagerState = rememberPagerState {
357+
2
358+
}
359+
HorizontalPager(state = pagerState) { page ->
356360

357-
// Don't do looped operations in actual code, it's for demonstration
358-
items(items = currentBackStack.reversed()) {
361+
val headerText = if (page == 0) "Current Back stack(reversed)" else "Current hierarchy"
362+
Column {
359363
Text(
360-
text = it.destination.route
361-
?.replace("$packageName.", "")
362-
?.replace(
363-
"BottomNavigationRoute.",
364-
""
365-
) ?: it.destination.displayName,
366364
modifier = Modifier
367-
.shadow(4.dp, RoundedCornerShape(8.dp))
368-
.background(Color.White)
369365
.fillMaxWidth()
370-
.padding(16.dp),
371-
fontSize = 18.sp
366+
.padding(bottom = 8.dp),
367+
text = headerText,
368+
fontSize = 20.sp,
369+
fontWeight = FontWeight.Bold
372370
)
371+
372+
val destinations = if (page == 0) {
373+
currentBackStack.reversed().map { it.destination }
374+
} else {
375+
navController.currentDestination?.hierarchy?.toList() ?: listOf()
376+
}
377+
378+
LazyColumn(
379+
modifier = Modifier.fillMaxSize(),
380+
verticalArrangement = Arrangement.spacedBy(8.dp)
381+
) {
382+
383+
items(items = destinations) { destination: NavDestination ->
384+
385+
if (destination is NavGraph) {
386+
MainText(destination, packageName)
387+
destination.nodes.forEach { _, value ->
388+
SubItemText(value, packageName)
389+
}
390+
391+
} else {
392+
MainText(destination, packageName)
393+
}
394+
}
395+
}
396+
373397
}
374398
}
375399
}
376400
}
401+
402+
@SuppressLint("RestrictedApi")
403+
@Composable
404+
private fun SubItemText(value: NavDestination, packageName: String?) {
405+
Text(
406+
text = value.route
407+
?.replace("$packageName.", "")
408+
?.replace("BottomNavigationRoute.", "")
409+
?: value.displayName,
410+
modifier = Modifier
411+
.padding(start = 8.dp, bottom = 2.dp)
412+
.shadow(2.dp, RoundedCornerShape(8.dp))
413+
.background(Color.White)
414+
.fillMaxWidth()
415+
.padding(8.dp),
416+
fontSize = 12.sp
417+
)
418+
}
419+
420+
@SuppressLint("RestrictedApi")
421+
@Composable
422+
private fun MainText(destination: NavDestination, packageName: String?) {
423+
Text(
424+
text = destination.route
425+
?.replace("$packageName.", "")
426+
?.replace("BottomNavigationRoute.", "")
427+
?: destination.displayName,
428+
modifier = Modifier
429+
.shadow(4.dp, RoundedCornerShape(8.dp))
430+
.background(Color.White)
431+
.fillMaxWidth()
432+
.padding(16.dp),
433+
fontSize = 18.sp
434+
)
435+
}

0 commit comments

Comments
 (0)