- 添加依赖:implementation “androidx.navigation:navigation-compose:$nav_version”
Navigation for Compose
class MainActivity : AppCompatActivity() {
var theme: BloomTheme by mutableStateOf(BloomTheme.LIGHT)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppNavigation()
}
}
}
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("welcome") {
WelcomeScreen(navController = navController)
}
composable("login") {
LoginScreen(navController = navController)
}
composable("home") {
HomeScreen(navController = navController)
}
}
}
fun WelcomeScreen(navController: NavController) {
...
Button(onClick = {
navController.navigate("login")
navController.navigate("home"){
popUpTo("welcome")
}
navController.navigate("home"){
popUpTo("welcome"){ inclusive = true}
}
navController.navigate("home"){
launchSingleTop = true
}
}) {
Text(text = "Login in")
}
...
}
导航时携带参数
NavHost(...) {
composable(
"plantDetail/{plantId}/{fromBanner}",
arguments = listOf(
navArgument("plantId"){type=NavType.IntType},
navArgument("fromBanner"){type=NavType.BoolType}
)
)
}
NavHost(...) {
composable(
"plantDetail/{plantId}?fromBanner={fromBanner}",
arguments = listOf(
navArgument("fromBanner"){
type=NavType.BoolType
defaultValue = false
}
)
)
}
- 推荐用Id之类的索引参数传递,然后从本地或远程数据源请求
- 尽量为导航参数添加默认值
sealed class Screen(
val route: String,
@StringRes val resourceId: Int,
val icon: ImageVector
){
object Home:Screen("home",R.string.home, Icons.Filled.Home)
object Favorite:Screen("home",R.string.favorite, Icons.Filled.Favorite)
object Profie:Screen("home",R.string.profie, Icons.Filled.Profie)
object Cart:Screen("home",R.string.cart, Icons.Filled.Cart)
}
val items = listOf(Screen.Home,Screen.Favorite,Screen.Profie,Screen.Cart)
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation{
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(imageVector = screen.icon,
contentDescription = null) },
label = { Text(text = stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
selectedContentColor = Color.Red,
unselectedContentColor = Color.Gray,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = false
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = false
}
}
)
}
}
}
) { innerPadding ->
NavHost(navController, startDestination = Screen.Home.route, Modifier.padding(innerPadding)) {
composable(Screen.Home.route) { HomeScreen(navController) }
composable(Screen.Favorite.route) { FavoriteScreen(navController) }
composable(Screen.Cart.route) { CartScreen(navController) }
composable(Screen.Profile.route) { ProfileScreen(navController) }
}
}
嵌套导航图 Nested Navigation Graph
- 一个App Module 由多个Lib Module组成
- 添加lib依赖
- 设置根NavHost和子NavHost
导航DeepLinks
- 跨模块跳转或者跨进程跳转,最好用这个
依赖注入
待补充
sealed class Screen(
val route: String,
val title: String,
val icon: ImageVector
) {
object Home : Screen("home", "home", Icons.Filled.Home)
object Favorite : Screen("favorite", "favorite", Icons.Filled.Favorite)
object Notification : Screen("notification", "notification", Icons.Filled.Notifications)
object Cart : Screen("cart", "cart", Icons.Filled.ShoppingCart)
}
val items = listOf(Screen.Home, Screen.Favorite, Screen.Notification, Screen.Cart)
val uri = "android-app://compose.learn"
@Preview
@Composable
fun Navigation2() {
val navController = rememberNavController()
Scaffold(bottomBar = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
BottomNavigationItem(selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
Icon(screen.icon, contentDescription = "")
},
label = { Text(text = screen.title) })
}
}
}) { innerPadding ->
//deep link test: adb shell am start -d "android-app://compose.learn/notification/adbTest" -a android.intent.action.VIEW
NavHost(navController = navController, startDestination = Screen.Home.route, modifier = Modifier.padding(innerPadding)) {
composable(Screen.Home.route) { navBackStackEntry ->
//viewModel()以composable为ViewModelStore,作用域只在当前的composable中
val exampleViewModel: ExampleViewModel = viewModel()
//多个destination之间共享viewmodel可以使用以下方法传入一个公共的ViewModelStoreOwner,这里是navBackStackEntry, 在多层路由下可以这样使用
val exampleViewModel2 = hiltViewModel<ExampleViewModel2>(navBackStackEntry)
Home()
}
composable(Screen.Favorite.route) { Favorite() }
composable(
Screen.Notification.route,
deepLinks = listOf(navDeepLink { uriPattern = "$uri/notification/{from}" })
) { backStackEntry ->
Notification(backStackEntry.arguments?.getString("from"))
}
composable(Screen.Cart.route) { Cart() }
}
}
}
@Composable
fun Home() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(text = "Home")
}
}
@Composable
fun Favorite() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(text = "Favorite")
}
}
@Composable
fun Notification(from: String?) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(text = "Notification from $from")
}
}
@Composable
fun Cart() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(text = "Cart")
}
}
class ExampleViewModel : ViewModel() {
}
@HiltViewModel
class ExampleViewModel2 @Inject constructor() : ViewModel() {
}