Created
March 10, 2025 23:25
-
-
Save sajjadyousefnia/7e9c6a525de15e1ce8cc18e8a98d0978 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.runtime.mutableStateOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.geometry.isEmpty | |
import androidx.hilt.navigation.compose.hiltViewModel | |
import androidx.navigation.NamedNavArgument | |
import androidx.navigation.NavBackStackEntry | |
import androidx.navigation.NavDeepLink | |
import androidx.navigation.NavGraphBuilder | |
import androidx.navigation.NavHostController | |
import androidx.navigation.compose.NavHost | |
import androidx.navigation.compose.composable | |
import androidx.navigation.compose.rememberNavController | |
import androidx.navigation.contains | |
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 | |
import kotlin.collections.indexOf | |
import kotlin.collections.last | |
import kotlin.collections.lastIndex | |
import kotlin.collections.take | |
import kotlin.collections.toList | |
import kotlin.collections.toMutableList | |
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 | |
) | |
} | |
is NavigationEvent.PopUntil -> { | |
customNavigationStackManager.popUntil( | |
event.route.route, | |
event.inclusive | |
) | |
} | |
} | |
} | |
// another file | |
package com.divadventure.divadventure | |
sealed class NavigationEvent { | |
// data class NavigateTo(val screen: Screen) : NavigationEvent() | |
// data class NavigateBackTo(val popUpToRoute: String) : NavigationEvent() | |
// data class NavigateToAndPop(val screen: Screen, val popUpTo: Screen) : NavigationEvent() | |
data class Push(val screen: Screen) : NavigationEvent() | |
data class Pop(val screen: Screen) : NavigationEvent() | |
data class PushPop(val pushScreen: Screen, val popScreens: List<Screen>) : NavigationEvent() | |
data class PopUntil(val route: Screen, val inclusive: Boolean = false) : NavigationEvent( | |
) | |
} | |
// 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 -> { | |
pushPop( | |
screen = Screen.SignUpScreen, popUpTo = listOf( | |
Screen.LandingScreen | |
) | |
) | |
} | |
NavigationIntent.navigateLoginToLanding -> { | |
} | |
NavigationIntent.navigateSignupToLanding -> { | |
} | |
NavigationIntent.navaigteSignupToHome -> { | |
pushPop( | |
screen = Screen.HomeScreen, popUpTo = listOf( | |
Screen.SignUpScreen, Screen.LandingScreen, Screen.SplashScreen | |
) | |
) | |
} | |
NavigationIntent.navigateSplashToLanding -> { | |
pushPop( | |
screen = Screen.LandingScreen, popUpTo = listOf( | |
Screen.SplashScreen | |
) | |
) | |
} | |
} | |
} | |
} | |
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