Skip to content

Commit 8e72e08

Browse files
committed
ui: global bottom nav with animated transitions
1 parent 9bdb442 commit 8e72e08

File tree

2 files changed

+138
-76
lines changed

2 files changed

+138
-76
lines changed
Lines changed: 138 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
package com.appcontrolx.ui.navigation
22

33
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.WindowInsets
45
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.animation.EnterTransition
8+
import androidx.compose.animation.ExitTransition
9+
import androidx.compose.animation.fadeIn
10+
import androidx.compose.animation.fadeOut
11+
import androidx.compose.animation.slideInHorizontally
12+
import androidx.compose.animation.slideOutHorizontally
13+
import androidx.compose.material.icons.Icons
14+
import androidx.compose.material.icons.filled.Build
15+
import androidx.compose.material.icons.filled.Home
16+
import androidx.compose.material.icons.filled.Settings
517
import androidx.compose.material3.CircularProgressIndicator
18+
import androidx.compose.material3.Icon
19+
import androidx.compose.material3.NavigationBar
20+
import androidx.compose.material3.NavigationBarItem
21+
import androidx.compose.material3.Scaffold
22+
import androidx.compose.material3.Text
623
import androidx.compose.runtime.Composable
724
import androidx.compose.runtime.getValue
825
import androidx.compose.ui.Alignment
926
import androidx.compose.ui.Modifier
1027
import androidx.hilt.navigation.compose.hiltViewModel
1128
import androidx.lifecycle.compose.collectAsStateWithLifecycle
29+
import androidx.navigation.compose.currentBackStackEntryAsState
1230
import androidx.navigation.NavHostController
1331
import androidx.navigation.NavGraph.Companion.findStartDestination
1432
import androidx.navigation.compose.NavHost
@@ -41,68 +59,136 @@ fun AppNavGraph(
4159
}
4260

4361
val startDestination = if (isSetupCompleted == true) Screen.Dashboard.route else Screen.Setup.route
62+
val navBackStackEntry by navController.currentBackStackEntryAsState()
63+
val currentRoute = navBackStackEntry?.destination?.route
4464

45-
NavHost(
46-
navController = navController,
47-
startDestination = startDestination
48-
) {
49-
composable(Screen.Setup.route) {
50-
SetupScreen(
51-
onComplete = {
52-
mainViewModel.completeSetup()
53-
navController.navigate(Screen.Dashboard.route) {
54-
popUpTo(Screen.Setup.route) { inclusive = true }
55-
}
65+
val showBottomBar = currentRoute != null && currentRoute != Screen.Setup.route
66+
val toolsRoutes = setOf(Screen.Tools.route, Screen.Apps.route, Screen.ActivityLauncher.route)
67+
val settingsRoutes = setOf(Screen.Settings.route, Screen.About.route)
68+
69+
val enterTransition: androidx.compose.animation.AnimatedContentTransitionScope<*>.() -> EnterTransition = {
70+
slideInHorizontally(initialOffsetX = { fullWidth -> fullWidth / 3 }) + fadeIn()
71+
}
72+
val exitTransition: androidx.compose.animation.AnimatedContentTransitionScope<*>.() -> ExitTransition = {
73+
slideOutHorizontally(targetOffsetX = { fullWidth -> -fullWidth / 6 }) + fadeOut()
74+
}
75+
val popEnterTransition: androidx.compose.animation.AnimatedContentTransitionScope<*>.() -> EnterTransition = {
76+
slideInHorizontally(initialOffsetX = { fullWidth -> -fullWidth / 3 }) + fadeIn()
77+
}
78+
val popExitTransition: androidx.compose.animation.AnimatedContentTransitionScope<*>.() -> ExitTransition = {
79+
slideOutHorizontally(targetOffsetX = { fullWidth -> fullWidth / 6 }) + fadeOut()
80+
}
81+
82+
Scaffold(
83+
contentWindowInsets = WindowInsets(0, 0, 0, 0),
84+
bottomBar = {
85+
if (showBottomBar) {
86+
NavigationBar {
87+
NavigationBarItem(
88+
icon = { Icon(Icons.Default.Home, contentDescription = null) },
89+
label = { Text("Home") },
90+
selected = currentRoute == Screen.Dashboard.route,
91+
onClick = {
92+
navController.navigate(Screen.Dashboard.route) {
93+
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
94+
launchSingleTop = true
95+
restoreState = true
96+
}
97+
}
98+
)
99+
NavigationBarItem(
100+
icon = { Icon(Icons.Default.Build, contentDescription = null) },
101+
label = { Text("Tools") },
102+
selected = currentRoute in toolsRoutes,
103+
onClick = {
104+
navController.navigate(Screen.Tools.route) {
105+
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
106+
launchSingleTop = true
107+
restoreState = true
108+
}
109+
}
110+
)
111+
NavigationBarItem(
112+
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
113+
label = { Text("Settings") },
114+
selected = currentRoute in settingsRoutes,
115+
onClick = {
116+
navController.navigate(Screen.Settings.route) {
117+
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
118+
launchSingleTop = true
119+
restoreState = true
120+
}
121+
}
122+
)
56123
}
57-
)
124+
}
58125
}
126+
) { innerPadding ->
127+
NavHost(
128+
navController = navController,
129+
startDestination = startDestination,
130+
modifier = Modifier.padding(innerPadding),
131+
enterTransition = enterTransition,
132+
exitTransition = exitTransition,
133+
popEnterTransition = popEnterTransition,
134+
popExitTransition = popExitTransition
135+
) {
136+
composable(Screen.Setup.route) {
137+
SetupScreen(
138+
onComplete = {
139+
mainViewModel.completeSetup()
140+
navController.navigate(Screen.Dashboard.route) {
141+
popUpTo(Screen.Setup.route) { inclusive = true }
142+
}
143+
}
144+
)
145+
}
59146

60-
composable(Screen.Dashboard.route) {
61-
DashboardScreen(
62-
onNavigateToApps = { navController.navigate(Screen.Apps.route) },
63-
onNavigateToTools = { navController.navigate(Screen.Tools.route) },
64-
onNavigateToSettings = { navController.navigate(Screen.Settings.route) }
65-
)
66-
}
147+
composable(Screen.Dashboard.route) {
148+
DashboardScreen(
149+
onNavigateToApps = { navController.navigate(Screen.Apps.route) }
150+
)
151+
}
67152

68-
composable(Screen.Apps.route) {
69-
AppListScreen(
70-
onNavigateBack = { navController.popBackStack() }
71-
)
72-
}
153+
composable(Screen.Apps.route) {
154+
AppListScreen(
155+
onNavigateBack = { navController.popBackStack() }
156+
)
157+
}
73158

74-
composable(Screen.Tools.route) {
75-
ToolsScreen(
76-
onNavigateToApps = { navController.navigate(Screen.Apps.route) },
77-
onNavigateToActivityLauncher = { navController.navigate(Screen.ActivityLauncher.route) },
78-
onNavigateBack = { navController.popBackStack() }
79-
)
80-
}
159+
composable(Screen.Tools.route) {
160+
ToolsScreen(
161+
onNavigateToApps = { navController.navigate(Screen.Apps.route) },
162+
onNavigateToActivityLauncher = { navController.navigate(Screen.ActivityLauncher.route) },
163+
onNavigateBack = { navController.popBackStack() }
164+
)
165+
}
81166

82-
composable(Screen.ActivityLauncher.route) {
83-
ActivityLauncherScreen(
84-
onNavigateBack = { navController.popBackStack() }
85-
)
86-
}
167+
composable(Screen.ActivityLauncher.route) {
168+
ActivityLauncherScreen(
169+
onNavigateBack = { navController.popBackStack() }
170+
)
171+
}
87172

88-
composable(Screen.Settings.route) {
89-
SettingsScreen(
90-
onNavigateToAbout = { navController.navigate(Screen.About.route) },
91-
onNavigateBack = { navController.popBackStack() },
92-
onResetSetup = {
93-
mainViewModel.resetSetup()
94-
navController.navigate(Screen.Setup.route) {
95-
popUpTo(navController.graph.findStartDestination().id) { inclusive = true }
96-
launchSingleTop = true
173+
composable(Screen.Settings.route) {
174+
SettingsScreen(
175+
onNavigateToAbout = { navController.navigate(Screen.About.route) },
176+
onNavigateBack = { navController.popBackStack() },
177+
onResetSetup = {
178+
mainViewModel.resetSetup()
179+
navController.navigate(Screen.Setup.route) {
180+
popUpTo(navController.graph.findStartDestination().id) { inclusive = true }
181+
launchSingleTop = true
182+
}
97183
}
98-
}
99-
)
100-
}
184+
)
185+
}
101186

102-
composable(Screen.About.route) {
103-
AboutScreen(
104-
onNavigateBack = { navController.popBackStack() }
105-
)
187+
composable(Screen.About.route) {
188+
AboutScreen(
189+
onNavigateBack = { navController.popBackStack() }
190+
)
191+
}
106192
}
107193
}
108194
}

app/src/main/java/com/appcontrolx/ui/screens/dashboard/DashboardScreen.kt

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ import com.appcontrolx.ui.screens.dashboard.DashboardViewModel
3030
@Composable
3131
fun DashboardScreen(
3232
onNavigateToApps: () -> Unit,
33-
onNavigateToTools: () -> Unit,
34-
onNavigateToSettings: () -> Unit,
3533
viewModel: DashboardViewModel = hiltViewModel()
3634
) {
3735
val systemStats by viewModel.systemStats.collectAsStateWithLifecycle()
@@ -54,28 +52,6 @@ fun DashboardScreen(
5452
containerColor = Color.Transparent
5553
)
5654
)
57-
},
58-
bottomBar = {
59-
NavigationBar {
60-
NavigationBarItem(
61-
icon = { Icon(Icons.Default.Home, contentDescription = null) },
62-
label = { Text("Home") },
63-
selected = true,
64-
onClick = { }
65-
)
66-
NavigationBarItem(
67-
icon = { Icon(Icons.Default.Build, contentDescription = null) },
68-
label = { Text("Tools") },
69-
selected = false,
70-
onClick = onNavigateToTools
71-
)
72-
NavigationBarItem(
73-
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
74-
label = { Text("Settings") },
75-
selected = false,
76-
onClick = onNavigateToSettings
77-
)
78-
}
7955
}
8056
) { paddingValues ->
8157
if (isLoading) {

0 commit comments

Comments
 (0)