Skip to content

Instantly share code, notes, and snippets.

@sajjadyousefnia
Created March 10, 2025 18:41
Show Gist options
  • Save sajjadyousefnia/a21ce579b5d0108288bbecdcb3665016 to your computer and use it in GitHub Desktop.
Save sajjadyousefnia/a21ce579b5d0108288bbecdcb3665016 to your computer and use it in GitHub Desktop.
package com.divadventure.divadventure
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.divadventure.divadventure.ViewModel.AuthViewModel
import com.divadventure.divadventure.ViewModel.NavigationViewModel
import com.divadventure.divadventure.ui.AuthScreen
import com.divadventure.divadventure.ui.HomeScreen
import com.divadventure.divadventure.ui.LandingScreen
import com.divadventure.divadventure.ui.SignUpScreen
import com.divadventure.divadventure.ui.Splash
sealed interface Screen {
val route: String
object SplashScreen : Screen {
override val route: String = "splash_screen"
}
object LandingScreen : Screen {
override val route: String = "landing_screen"
}
object LoginScreen : Screen {
override val route: String = "login_screen"
}
object SignUpScreen : Screen {
override val route: String = "sign_up_screen"
}
object RegisterScreen : Screen {
override val route: String = "register_screen"
}
object ForgotPasswordScreen : Screen {
override val route: String = "forgot_password_screen"
}
object EmailVerificationScreen : Screen {
override val route: String = "email_verification_screen"
}
object HomeScreen : Screen {
override val route: String = "home_screen"
}
object ProfileScreen : Screen {
override val route: String = "profile_screen"
}
}
@Composable
fun AppNavigation(padding: PaddingValues) {
val navController: NavHostController = rememberNavController()
val navigationViewModel: NavigationViewModel = hiltViewModel()
val authViewModel: AuthViewModel = hiltViewModel()
// Listen for navigation events from NavigationViewModel
val customNavigationStackManager = CustomNavigationStackManager(navController)
LaunchedEffect(key1 = true) {
navigationViewModel.navigationEvent.collect { event ->
handleNavigationEvent(event, customNavigationStackManager)
}
}
// Listen for navigation events from AuthViewModel
LaunchedEffect(key1 = true) {
authViewModel.navigationEvent.collect { event ->
handleNavigationEvent(event, customNavigationStackManager)
}
}
NavHost(
modifier = Modifier.padding(padding),
navController = navController,
startDestination = Screen.SplashScreen.route,
route = "root_graph"
) {
composable(Screen.SplashScreen.route) {
Splash(
viewModel = navigationViewModel, //The splash screen uses the NavigationViewModel
navController = navController
)
}
composable(Screen.LandingScreen.route) {
LandingScreen(
authViewModel,
navController,
navigationViewModel
)
}
composable(Screen.LoginScreen.route) {
AuthScreen(
navController = navController,
viewModel = authViewModel
)
}
composable(Screen.SignUpScreen.route) {
SignUpScreen(
padding = padding,
viewModel = authViewModel,
navigationViewModel = navigationViewModel
)
}
composable(Screen.HomeScreen.route) {
HomeScreen()
}
}
}
private fun handleNavigationEvent(
event: NavigationEvent,
customNavigationStackManager: CustomNavigationStackManager
) {
when (event) {
is NavigationEvent.Pop -> {
customNavigationStackManager.popSpecific(
event.screen.route
)
}
is NavigationEvent.Push -> {
customNavigationStackManager.push(
event.screen.route
)
}
is NavigationEvent.PushPop -> {
event.popScreens.forEach {
customNavigationStackManager.popSpecific(
it.route
)
}
customNavigationStackManager.push(
event.pushScreen.route
)
}
}
}
// another file
package com.divadventure.divadventure.ViewModel
import androidx.lifecycle.viewModelScope
import com.divadventure.divadventure.BaseViewModel
import com.divadventure.divadventure.NavigationEvent
import com.divadventure.divadventure.Screen
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class NavigationViewModel @Inject constructor() :
BaseViewModel<NavigationIntent, NavigationState>(NavigationState()) {
private val _currentScreen = MutableStateFlow<Screen>(Screen.LandingScreen)
val currentScreen: StateFlow<Screen> = _currentScreen.asStateFlow()
private val _isBottomBarVisible = MutableStateFlow(true)
val isBottomBarVisible: StateFlow<Boolean> = _isBottomBarVisible.asStateFlow()
fun showBottomBar() {
_isBottomBarVisible.update { true }
}
fun hideBottomBar() {
_isBottomBarVisible.update { false }
}
// This is now correct
private fun navigateTo(screen: Screen) {
viewModelScope.launch {
_currentScreen.value = screen // Update the state
_navigationEvent.send(NavigationEvent.Push(screen)) // Emit the event
}
}
private fun navigateToAndPop(screen: Screen, popUpTo: Screen) {
viewModelScope.launch {
_currentScreen.value = screen
_navigationEvent.send(NavigationEvent.PushPop(screen, listOf()))
/*_navigationEvent.send(NavigationEvent.Pop(popUpTo))*/
}
}
private fun pushPop(screen: Screen, popUpTo: List<Screen>) {
viewModelScope.launch {
_currentScreen.value = screen
_navigationEvent.send(NavigationEvent.PushPop(screen, popUpTo))
}
}
override suspend fun handleIntent(intent: NavigationIntent) {
when (intent) {
NavigationIntent.navigateLandingToLogin -> {
}
NavigationIntent.navigateLandingToSignup -> {
navigateTo(Screen.SignUpScreen)
}
NavigationIntent.navigateLoginToLanding -> {
}
NavigationIntent.navigateSignupToLanding -> {
}
NavigationIntent.navaigteSignupToHome -> {
pushPop(
screen = Screen.HomeScreen, popUpTo = listOf(
Screen.SignUpScreen,
Screen.LandingScreen, Screen.SplashScreen
)
)
}
NavigationIntent.navigateSplashToLanding -> {
navigateTo(Screen.LandingScreen)
}
}
}
}
sealed class NavigationIntent {
object navigateSplashToLanding : NavigationIntent()
object navaigteSignupToHome : NavigationIntent()
object navigateLandingToLogin : NavigationIntent()
object navigateLoginToLanding : NavigationIntent()
object navigateLandingToSignup : NavigationIntent()
object navigateSignupToLanding : NavigationIntent()
}
data class NavigationState(val value: String = "")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment