14. Navigation dans Jetpack Compose¶
Exemple avec des boutons pour naviguer entre les composables¶
Configuration initiale¶
Ajoutez d’abord la dépendance dans le fichier build.gradle :
Structure de l’exemple¶
Nous allons créer une application avec trois écrans :
- Écran d’accueil
- Écran de profil
- Écran de paramètres
Définition des routes¶
sealed class Screen(val route: String) {
object Home : Screen("home")
object Profile : Screen("profile")
object Settings : Screen("settings")
}
Création des composables d’écran¶
@Composable
fun HomeScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Écran d'accueil")
Button(onClick = { navController.navigate(Screen.Profile.route) }) {
Text("Aller au profil")
}
Button(onClick = { navController.navigate(Screen.Settings.route) }) {
Text("Aller aux paramètres")
}
}
}
@Composable
fun ProfileScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Écran de profil")
Button(onClick = { navController.navigate(Screen.Settings.route) }) {
Text("Aller aux paramètres")
}
Button(onClick = { navController.popBackStack() }) {
Text("Retour")
}
}
}
@Composable
fun SettingsScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Écran des paramètres")
Button(onClick = { navController.popBackStack() }) {
Text("Retour")
}
Button(
onClick = {
navController.navigate(Screen.Home.route) {
popUpTo(Screen.Home.route) { inclusive = true }
}
}
) {
Text("Retour à l'accueil")
}
}
}
Configuration de la navigation¶
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Screen.Home.route
) {
composable(Screen.Home.route) {
HomeScreen(navController)
}
composable(Screen.Profile.route) {
ProfileScreen(navController)
}
composable(Screen.Settings.route) {
SettingsScreen(navController)
}
}
}
Intégration dans MainActivity¶
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MonTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
AppNavigation()
}
}
}
}
}
Fonctionnalités démontrées¶
- Navigation de base : Navigation entre les écrans avec
navigate()[1] - Navigation arrière : Utilisation de
popBackStack()[2] - Navigation avec effacement de pile : Utilisation de
popUpTopour retourner à l’accueil[2]
Points importants¶
- Le
NavControllergère l’état de la navigation[1] - Le
NavHostdéfinit le graphe de navigation[1][2] - Chaque écran reçoit le
NavControllerpour gérer la navigation[3] popBackStack()permet de revenir à l’écran précédent[2]popUpToavecinclusive = truepermet de remonter jusqu’à une destination en l’incluant dans la suppression[5]
Cet exemple montre les bases de la navigation dans Jetpack Compose tout en restant simple et compréhensible pour des débutants.
Citations: [1] https://proandroiddev.com/android-jetpack-compose-navigation-1cdfc488b891?gi=d31e0323b815 [2] https://saurabhjadhavblogs.com/ultimate-guide-to-jetpack-compose-navigation [3] https://blog.kotlin-academy.com/mastery-navigation-in-jetpack-compose-db00b0a0ef75?gi=007e50484ede [4] https://developer.android.com/develop/ui/compose/navigation [5] https://proandroiddev.com/mastering-navigation-in-jetpack-compose-a-guide-to-using-the-inclusive-attribute-b66916a5f15c?gi=401071494588 [6] https://developer.android.com/codelabs/basic-android-kotlin-compose-navigation [7] https://developer.android.com/develop/ui/compose/navigation?hl=fr [8] https://www.youtube.com/watch?v=AIC_OFQ1r3k
Exemple avec un menu dans la barre supérieure de l’application¶
Structure de l’application¶
sealed class Screen(val route: String, val title: String) {
object Home : Screen("home", "Accueil")
object Profile : Screen("profile", "Profil")
object Settings : Screen("settings", "Paramètres")
}
Configuration de la navigation¶
@Composable
fun AppNavigation() {
val navController = rememberNavController()
Scaffold(
topBar = {
TopAppBarWithMenu(navController)
}
) { paddingValues ->
NavHost(
navController = navController,
startDestination = Screen.Home.route,
modifier = Modifier.padding(paddingValues)
) {
composable(Screen.Home.route) {
HomeScreen()
}
composable(Screen.Profile.route) {
ProfileScreen()
}
composable(Screen.Settings.route) {
SettingsScreen()
}
}
}
}
TopBar avec menu¶
@Composable
fun TopAppBarWithMenu(navController: NavController) {
var showMenu by remember { mutableStateOf(false) }
TopAppBar(
title = { Text("Mon Application") },
actions = {
IconButton(onClick = { showMenu = !showMenu }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu")
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false }
) {
DropdownMenuItem(
text = { Text(Screen.Home.title) },
onClick = {
navController.navigate(Screen.Home.route) {
popUpTo(Screen.Home.route) { inclusive = true }
}
showMenu = false
}
)
DropdownMenuItem(
text = { Text(Screen.Profile.title) },
onClick = {
navController.navigate(Screen.Profile.route)
showMenu = false
}
)
DropdownMenuItem(
text = { Text(Screen.Settings.title) },
onClick = {
navController.navigate(Screen.Settings.route)
showMenu = false
}
)
}
}
)
}
Écrans de l’application¶
@Composable
fun HomeScreen() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Écran d'accueil")
}
}
@Composable
fun ProfileScreen() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Écran de profil")
}
}
@Composable
fun SettingsScreen() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("Écran des paramètres")
}
}
Points importants à noter¶
-
Le menu est géré par un état
showMenuqui contrôle l’affichage duDropdownMenu -
Chaque item du menu utilise
navController.navigate()pour la navigation -
Pour l’écran d’accueil, nous utilisons
popUpToavecinclusive = truepour éviter l’accumulation d’écrans dans la pile de navigation -
Le
Scaffoldgère automatiquement le padding nécessaire pour éviter que le contenu ne soit caché derrière la TopBar -
Les écrans sont simples mais peuvent être enrichis selon les besoins de l’application
Cette implémentation offre une navigation claire et intuitive via un menu déroulant dans la barre supérieure de l’application.
Citations: [1] https://saurabhjadhavblogs.com/ultimate-guide-to-jetpack-compose-navigation [2] https://proandroiddev.com/android-jetpack-compose-navigation-1cdfc488b891?gi=d31e0323b815 [3] https://proandroiddev.com/implement-bottom-bar-navigation-in-jetpack-compose-b530b1cd9ee2?gi=5c1ab6e9d027 [4] https://proandroiddev.com/mastering-navigation-in-jetpack-compose-a-guide-to-using-the-inclusive-attribute-b66916a5f15c?gi=401071494588 [5] https://blog.kotlin-academy.com/mastery-navigation-in-jetpack-compose-db00b0a0ef75?gi=007e50484ede [6] https://developer.android.com/codelabs/basic-android-kotlin-compose-navigation [7] https://www.youtube.com/watch?v=JLICaBEiJS0 [8] https://developer.android.com/develop/ui/compose/navigation
Exemple avec des icônes dans la barre inférieure¶
Structure de l’application¶
sealed class Screen(
val route: String,
val title: String,
val icon: ImageVector
) {
object Home : Screen(
route = "home",
title = "Accueil",
icon = Icons.Default.Home
)
object Profile : Screen(
route = "profile",
title = "Profil",
icon = Icons.Default.Person
)
object Settings : Screen(
route = "settings",
title = "Paramètres",
icon = Icons.Default.Settings
)
companion object {
val items = listOf(Home, Profile, Settings)
}
}
Configuration de la navigation¶
@Composable
fun AppNavigation() {
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
Scaffold(
topBar = {
TopAppBar(
title = { Text("Mon Application") }
)
},
bottomBar = {
NavigationBar {
Screen.items.forEach { screen ->
NavigationBarItem(
icon = {
Icon(screen.icon, contentDescription = screen.title)
},
label = { Text(screen.title) },
selected = currentRoute == screen.route,
onClick = {
navController.navigate(screen.route) {
// Évite l'empilement des destinations
popUpTo(navController.graph.startDestinationId) {
saveState = true
}
// Évite les copies multiples de la même destination
launchSingleTop = true
// Restaure l'état lors de la reselection
restoreState = true
}
}
)
}
}
}
) { paddingValues ->
NavHost(
navController = navController,
startDestination = Screen.Home.route,
modifier = Modifier.padding(paddingValues)
) {
composable(Screen.Home.route) {
HomeScreen()
}
composable(Screen.Profile.route) {
ProfileScreen()
}
composable(Screen.Settings.route) {
SettingsScreen()
}
}
}
}
Écrans de l’application¶
@Composable
fun HomeScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
Icons.Default.Home,
contentDescription = null,
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
"Écran d'accueil",
style = MaterialTheme.typography.headlineMedium
)
}
}
@Composable
fun ProfileScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
Icons.Default.Person,
contentDescription = null,
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
"Écran de profil",
style = MaterialTheme.typography.headlineMedium
)
}
}
@Composable
fun SettingsScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
Icons.Default.Settings,
contentDescription = null,
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
"Écran des paramètres",
style = MaterialTheme.typography.headlineMedium
)
}
}
Points importants¶
-
La
NavigationBarutiliseNavigationBarItempour chaque destination -
L’état
selectedest géré en comparant la route courante avec la route de l’item -
La navigation inclut des options importantes :
launchSingleTopévite les copies multiplespopUpToavecsaveStategère correctement la pile de navigationrestoreStatepréserve l’état lors de la reselection
-
Les icônes et les titres sont définis dans la classe scellée
Screen -
Le
currentBackStackEntryAsState()permet de suivre la destination actuelle
Cette implémentation offre une navigation fluide et intuitive avec une barre de navigation inférieure, couramment utilisée dans les applications mobiles modernes. Les utilisateurs peuvent facilement basculer entre les différentes sections de l’application en touchant les icônes correspondantes.