Created
January 3, 2021 19:46
-
-
Save nglauber/5061cbbafd63488131380b09c8943ad7 to your computer and use it in GitHub Desktop.
Bottom Navigation + Jetpack Navigation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.samplecomposenavigation | |
import android.os.Bundle | |
import androidx.compose.material.* | |
import androidx.compose.material.icons.Icons | |
import androidx.compose.material.icons.filled.List | |
import androidx.compose.material.icons.filled.Settings | |
import androidx.compose.runtime.* | |
import androidx.compose.runtime.savedinstancestate.Saver | |
import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState | |
import androidx.compose.ui.graphics.vector.ImageVector | |
import androidx.core.os.bundleOf | |
import androidx.navigation.NavController | |
import androidx.navigation.NavHostController | |
import androidx.navigation.compose.NavHost | |
import androidx.navigation.compose.composable | |
import androidx.navigation.compose.rememberNavController | |
import com.example.samplecomposenavigation.ui.SampleComposeNavigationTheme | |
sealed class TabItem(val route: String, val title: String, val icon: ImageVector) { | |
object TabListInfo : TabItem(SCREEN_LIST, "Items", Icons.Filled.List) | |
object TabProfileInfo : TabItem(SCREEN_PROFILE, "Profile", Icons.Filled.Settings) | |
fun saveState(): Bundle { | |
return bundleOf(KEY_SCREEN to route) | |
} | |
companion object { | |
val defaultTab = TabListInfo | |
fun screenSaver( | |
): Saver<MutableState<TabItem>, *> = Saver( | |
save = { it.value.saveState() }, | |
restore = { mutableStateOf(restoreState(it)) } | |
) | |
private fun restoreState(bundle: Bundle): TabItem { | |
return when (bundle.getString(KEY_SCREEN, defaultTab.route)) { | |
TabProfileInfo.route -> TabProfileInfo | |
TabListInfo.route -> TabListInfo | |
else -> defaultTab | |
} | |
} | |
const val KEY_SCREEN = "route" | |
} | |
} | |
@Composable | |
fun MainScreen() { | |
var currentTab by rememberSavedInstanceState( | |
saver = TabItem.screenSaver() | |
) { mutableStateOf(TabItem.defaultTab) } | |
val items = listOf( | |
TabItem.TabListInfo, | |
TabItem.TabProfileInfo, | |
) | |
SampleComposeNavigationTheme { | |
Scaffold( | |
bottomBar = { | |
BottomNavigation { | |
items.forEach { screen -> | |
BottomNavigationItem( | |
icon = { Icon(screen.icon) }, | |
label = { Text(screen.title) }, | |
selected = currentTab == screen, | |
onClick = { | |
currentTab = screen | |
} | |
) | |
} | |
} | |
} | |
) { | |
TabContent(currentTab) | |
} | |
} | |
} | |
@Composable | |
fun TabContent(tabItem: TabItem) { | |
val tab1NavState = rememberSavedInstanceState(saver = navStateSaver()) { mutableStateOf(Bundle()) } | |
val tab2NavState = rememberSavedInstanceState(saver = navStateSaver()) { mutableStateOf(Bundle()) } | |
when (tabItem) { | |
TabItem.TabListInfo -> { | |
TabWrapper(tab1NavState) { | |
TabList(it) | |
} | |
} | |
TabItem.TabProfileInfo -> { | |
TabWrapper(tab2NavState) { | |
TabProfile(it) | |
} | |
} | |
} | |
} | |
fun navStateSaver(): Saver<MutableState<Bundle>, out Any> = Saver( | |
save = { it.value }, | |
restore = { mutableStateOf(it) } | |
) | |
@Composable | |
fun TabWrapper( | |
navState: MutableState<Bundle>, | |
body: @Composable (NavHostController) -> Unit | |
) { | |
val navController = rememberNavController() | |
onCommit { | |
val callback = NavController.OnDestinationChangedListener { navController, _, _ -> | |
navState.value = navController.saveState() ?: Bundle() | |
} | |
navController.addOnDestinationChangedListener(callback) | |
navController.restoreState(navState.value) | |
onDispose { | |
navController.removeOnDestinationChangedListener(callback) | |
// workaround for issue where back press is intercepted | |
// outside this tab, even after this Composable is disposed | |
navController.enableOnBackPressed(false) | |
} | |
} | |
body(navController) | |
} | |
@Composable | |
fun TabList(navController: NavHostController) { | |
NavHost(navController = navController, startDestination = SCREEN_LIST) { | |
composable(SCREEN_LIST) { ListScreen(navController) } | |
composable("$SCREEN_DETAILS/{$PARAM_USER_NAME}") { backStackEntry -> | |
DetailScreen(backStackEntry.arguments?.getString(PARAM_USER_NAME) ?: "") | |
} | |
} | |
} | |
@Composable | |
fun TabProfile(navController: NavHostController) { | |
NavHost(navController = navController, startDestination = SCREEN_PROFILE) { | |
composable(SCREEN_PROFILE) { ProfileScreen(navController) } | |
composable(SCREEN_PROFILE_DETAIL) { ProfileDetailScreen() } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment