feat: add main nav host best practice

This commit is contained in:
2026-05-27 23:55:28 +03:30
parent c09d79dbbc
commit c8044bfe42
7 changed files with 137 additions and 169 deletions
@@ -36,9 +36,7 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
DrugTheme { DrugTheme {
val navController = rememberNavController()
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
AppNavGraph( AppNavGraph(
// navController = navController, // navController = navController,
// modifier = Modifier.padding(innerPadding) // modifier = Modifier.padding(innerPadding)
@@ -1,9 +1,18 @@
package com.approagency.drug.navigation package com.approagency.drug.navigation
import android.net.Uri
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.approagency.drug.presentation.screens.DrugDetailScreen
import com.approagency.drug.presentation.screens.HomeScreen
import com.approagency.drug.presentation.screens.LabScreen
import com.approagency.drug.presentation.screens.SearchScreen
@Composable @Composable
fun AppNavGraph() { fun AppNavGraph() {
@@ -12,12 +21,51 @@ fun AppNavGraph() {
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = "main_container" startDestination = "home"
) { ) {
composable("main_container") { // ───────────────
// BOTTOM TABS
// ───────────────
MainContainer() composable("home") {
MainContainer(navController) {
HomeScreen(navController , modifier = Modifier)
}
}
composable("search") {
MainContainer(navController) {
SearchScreen(navController)
}
}
composable("lab") {
MainContainer(navController) {
LabScreen()
}
}
// ───────────────
// GLOBAL SCREEN
// ───────────────
composable(
route = Screen.DrugDetail.route,
arguments = listOf(
navArgument("detailUrl") {
type = NavType.StringType
}
)
) {
val detailUrl =
Uri.decode(it.arguments?.getString("detailUrl") ?: "")
DrugDetailScreen(
navController = navController,
detailUrl = detailUrl
)
} }
} }
} }
@@ -39,62 +39,56 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
@Composable @Composable
fun BottomBar( fun BottomBar(navController: NavHostController) {
navController: NavHostController
) {
val items = listOf( val items = listOf(
Triple(MainRoute.HomeGraph, "دارو", Icons.Default.Search), Triple("home", "دارو", Icons.Default.Search),
Triple(MainRoute.SearchGraph, "سرچ", Icons.Rounded.Refresh), Triple("search", "سرچ", Icons.Rounded.Refresh),
Triple(MainRoute.LabGraph, "آزمایش", Icons.Default.Build) Triple("lab", "آزمایش", Icons.Default.Build)
) )
val navBackStackEntry by navController.currentBackStackEntryAsState() val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination val currentRoute = navBackStackEntry?.destination?.route
Surface( Surface(
tonalElevation = 2.dp, tonalElevation = 2.dp,
color = MaterialTheme.colorScheme.surface,
modifier = Modifier modifier = Modifier
.navigationBarsPadding() .navigationBarsPadding()
.height(50.dp) .height(56.dp)
) { ) {
Row( Row(modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
verticalAlignment = Alignment.CenterVertically
) {
items.forEach { item -> items.forEach { item ->
val selected = currentDestination val selected = currentRoute == item.first
?.hierarchy
?.any { it.route == item.first.route } == true
val color = if (selected) val color = if (selected)
MaterialTheme.colorScheme.primary MaterialTheme.colorScheme.primary
else else
MaterialTheme.colorScheme.onSurfaceVariant MaterialTheme.colorScheme.onSurfaceVariant
val background = if (selected) val background = if (selected)
MaterialTheme.colorScheme.primary.copy(alpha = 0.12f) MaterialTheme.colorScheme.primary.copy(alpha = 0.12f)
else else
Color.Transparent Color.Transparent
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f).padding(horizontal = 8.dp , vertical = 4.dp)
.padding(horizontal = 8.dp , vertical = 4.dp)
.clip(MaterialTheme.shapes.large) .clip(MaterialTheme.shapes.large)
.background(background) .background(background)
.clickable { .clickable {
navController.navigate(item.first.route) { if (!selected) {
popUpTo(navController.graph.findStartDestination().id) { navController.navigate(item.first) {
saveState = true popUpTo("home") {
saveState = true
}
launchSingleTop = true
restoreState = true
} }
launchSingleTop = true
restoreState = true
} }
} }.padding(horizontal = 10.dp, vertical = 10.dp),
.padding(horizontal = 10.dp, vertical = 10.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Row( Row(
@@ -1,51 +1,28 @@
package com.approagency.drug.navigation package com.approagency.drug.navigation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.approagency.drug.navigation.graphs.homeGraph
import com.approagency.drug.navigation.graphs.labGraph
import com.approagency.drug.navigation.graphs.searchGraph
@Composable @Composable
fun MainContainer() { fun MainContainer(
navController: NavHostController,
content: @Composable () -> Unit
) {
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
val showBottomBar = when (currentRoute) {
Screen.Home.route,
Screen.Search.route,
Screen.Lab.route -> true
else -> false
}
Scaffold( Scaffold(
bottomBar = { bottomBar = {
if (showBottomBar) {
BottomBar(navController) BottomBar(navController)
}
} }
) { padding -> ) { padding ->
NavHost( Box(modifier = Modifier.padding(padding)) {
navController = navController, content()
startDestination = MainRoute.HomeGraph.route,
modifier =if (showBottomBar ) Modifier.padding(padding) else Modifier
) {
homeGraph(navController)
searchGraph(navController)
labGraph(navController)
} }
} }
} }
@@ -1,28 +1,19 @@
package com.approagency.drug.navigation.graphs //package com.approagency.drug.navigation.graphs
//
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder //import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController //import androidx.navigation.NavHostController
import androidx.navigation.compose.composable //import androidx.navigation.compose.composable
import androidx.navigation.navigation //import androidx.navigation.navigation
import com.approagency.drug.navigation.MainRoute //import com.approagency.drug.navigation.MainRoute
import com.approagency.drug.navigation.Screen //import com.approagency.drug.navigation.Screen
import com.approagency.drug.presentation.screens.HomeScreen //import com.approagency.drug.presentation.screens.HomeScreen
//
fun NavGraphBuilder.homeGraph( //fun NavGraphBuilder.homeGraph(navController: NavHostController) {
navController: NavHostController //
) { // composable(MainRoute.HomeGraph.route) {
// HomeScreen(
navigation( // navController = navController , modifier = Modifier
route = MainRoute.HomeGraph.route, // )
startDestination = Screen.Home.route // }
) { //}
composable(Screen.Home.route) {
HomeScreen(
navController = navController , modifier = Modifier
)
}
}
}
@@ -1,25 +1,16 @@
package com.approagency.drug.navigation.graphs //package com.approagency.drug.navigation.graphs
//
import androidx.navigation.NavGraphBuilder //import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController //import androidx.navigation.NavHostController
import androidx.navigation.compose.composable //import androidx.navigation.compose.composable
import androidx.navigation.navigation //import androidx.navigation.navigation
import com.approagency.drug.navigation.MainRoute //import com.approagency.drug.navigation.MainRoute
import com.approagency.drug.navigation.Screen //import com.approagency.drug.navigation.Screen
import com.approagency.drug.presentation.screens.LabScreen //import com.approagency.drug.presentation.screens.LabScreen
//
fun NavGraphBuilder.labGraph( //fun NavGraphBuilder.labGraph(navController: NavHostController) {
navController: NavHostController //
) { // composable(MainRoute.LabGraph.route) {
// LabScreen()
navigation( // }
route = MainRoute.LabGraph.route, //}
startDestination = Screen.Lab.route
) {
composable(Screen.Lab.route) {
LabScreen()
}
}
}
@@ -1,51 +1,20 @@
package com.approagency.drug.navigation.graphs //package com.approagency.drug.navigation.graphs
//
import android.net.Uri //import android.net.Uri
import androidx.navigation.NavGraphBuilder //import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController //import androidx.navigation.NavHostController
import androidx.navigation.NavType //import androidx.navigation.NavType
import androidx.navigation.compose.composable //import androidx.navigation.compose.composable
import androidx.navigation.navArgument //import androidx.navigation.navArgument
import androidx.navigation.navigation //import androidx.navigation.navigation
import com.approagency.drug.navigation.MainRoute //import com.approagency.drug.navigation.MainRoute
import com.approagency.drug.navigation.Screen //import com.approagency.drug.navigation.Screen
import com.approagency.drug.presentation.screens.DrugDetailScreen //import com.approagency.drug.presentation.screens.DrugDetailScreen
import com.approagency.drug.presentation.screens.SearchScreen //import com.approagency.drug.presentation.screens.SearchScreen
//
fun NavGraphBuilder.searchGraph( //fun NavGraphBuilder.searchGraph(navController: NavHostController) {
navController: NavHostController //
) { // composable(MainRoute.SearchGraph.route) {
// SearchScreen(navController)
navigation( // }
route = MainRoute.SearchGraph.route, //}
startDestination = Screen.Search.route
) {
composable(Screen.Search.route) {
SearchScreen(
navController = navController
)
}
composable(
route = Screen.DrugDetail.route,
arguments = listOf(
navArgument("detailUrl") {
type = NavType.StringType
}
)
) {
val detailUrl =
Uri.decode(
it.arguments?.getString("detailUrl") ?: ""
)
DrugDetailScreen(
navController = navController,
detailUrl = detailUrl
)
}
}
}