Для управления навигацией мы можем использовать стандартные встроенные компоненты типа кнопок, определять свои компоненты, однако специально для целей навигации Compose также предоставляет специальный компонент - NavigationBar, который представляет навигационную панель. Каждый отдельный элемент этой панели представляет компонент NavigationBarItem.
Реализация NavigationBar обычно включает содержит цикл forEach, который проходит по списку и для каждого его элемента
создает отдельный компонент BNavigationBarItem. Для NavigationBarItem
настраивается иконку и текст, а также обработчик onClick для перехода
к соответствующему пункту назначения.
Рассмотрим небольшой примерчик:
package com.example.helloapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Face import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Info import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Main() } } } @Composable fun Main() { val navController = rememberNavController() Column(Modifier.padding(8.dp)) { NavHost(navController, startDestination = NavRoutes.Home.route, modifier = Modifier.weight(1f)) { composable(NavRoutes.Home.route) { Home() } composable(NavRoutes.Contacts.route) { Contacts() } composable(NavRoutes.About.route) { About() } } BottomNavigationBar(navController = navController) } } @Composable fun BottomNavigationBar(navController: NavController) { NavigationBar { val backStackEntry by navController.currentBackStackEntryAsState() val currentRoute = backStackEntry?.destination?.route NavBarItems.BarItems.forEach { navItem -> NavigationBarItem( selected = currentRoute == navItem.route, onClick = { navController.navigate(navItem.route) { popUpTo(navController.graph.findStartDestination().id) {saveState = true} launchSingleTop = true restoreState = true } }, icon = { Icon(imageVector = navItem.image, contentDescription = navItem.title) }, label = { Text(text = navItem.title) } ) } } } object NavBarItems { val BarItems = listOf( BarItem( title = "Home", image = Icons.Filled.Home, route = "home" ), BarItem( title = "Contacts", image = Icons.Filled.Face, route = "contacts" ), BarItem( title = "About", image = Icons.Filled.Info, route = "about" ) ) } data class BarItem( val title: String, val image: ImageVector, val route: String ) @Composable fun Home(){ Text("Home Page", fontSize = 30.sp) } @Composable fun Contacts(){ Text("Contact Page", fontSize = 30.sp) } @Composable fun About(){ Text("About Page", fontSize = 30.sp) } sealed class NavRoutes(val route: String) { object Home : NavRoutes("home") object Contacts : NavRoutes("contacts") object About : NavRoutes("about") }
Рассмотрим основные моменты. Прежде всего для представления отдельного элемента панели навигации определяем класс BarItem:
data class BarItem( val title: String, val image: ImageVector, val route: String )
Данный класс будет хранить текст ссылки навигации, иконку и маршрут для перехода к другому экрану.
Для представления набора ссылок навигации определяем объект NavBarItems, который содержит список из трех навигационных ссылок:
object NavBarItems { val BarItems = listOf( BarItem( title = "Home", image = Icons.Filled.Home, route = "home" ), BarItem( title = "Contacts", image = Icons.Filled.Face, route = "contacts" ), BarItem( title = "About", image = Icons.Filled.Info, route = "about" ) ) }
Для создания самой навигационной панели определяем компонент BottomNavigationBar:
@Composable fun BottomNavigationBar(navController: NavController) { NavigationBar { val backStackEntry by navController.currentBackStackEntryAsState() val currentRoute = backStackEntry?.destination?.route NavBarItems.BarItems.forEach { navItem -> NavigationBarItem( selected = currentRoute == navItem.route, onClick = { navController.navigate(navItem.route) { popUpTo(navController.graph.findStartDestination().id) {saveState = true} launchSingleTop = true restoreState = true } }, icon = { Icon(imageVector = navItem.image, contentDescription = navItem.title) }, label = { Text(text = navItem.title) } ) } } }
Фактически этот компонент является оберткой над NavigationBar, в который передает объект NavController для выполнения навигации.
Внутри BottomNavigationBar мы можем идентифицировать текущий маршрут с помощью метода currentBackStackEntryAsState()
контроллера навигации. Это позволит провести некоторую стилизацию
элементов панели на основе текущего маршрута:
val backStackEntry by navController.currentBackStackEntryAsState() val currentRoute = backStackEntry?.destination?.route
Далее проходим по всем элементам в списке и выводим каждый элемент с помощью компонента NavigationBarItem:
NavBarItems.BarItems.forEach { navItem -> NavigationBarItem( selected = currentRoute == navItem.route, onClick = { navController.navigate(navItem.route) { popUpTo(navController.graph.findStartDestination().id) {saveState = true} launchSingleTop = true restoreState = true } }, icon = { Icon(imageVector = navItem.image, contentDescription = navItem.title) }, label = { Text(text = navItem.title) } ) }
Обратите внимание, что при нажатии зедсь вызывается функция popUpTo()
, чтобы гарантировать, что если пользователь нажмет кнопку "Назад", навигация вернется к начальному пункту
назначения. Мы можем определить начальный пункт назначения, вызвав метод findStartDestination()
в графе навигации:
onClick = { navController.navigate(navItem.route) { popUpTo(navController.graph.findStartDestination().id) {saveState = true} launchSingleTop = true restoreState = true } },
Запустим приложение, и нашему взору предстанет очаровательная панель с иконками, нажав на которые мы сможем переходить к различным компонентами: