Hey everyone !! hope you doing grate , I am gett...
# compose
h
Hey everyone !! hope you doing grate , I am getting this error can anyone know how to solve it " FATAL EXCEPTION: main Process: com.hxt5gh.frontend, PID: 5173 java.lang.IllegalStateException: ViewModelStore should be set before setGraph call"
MainActivity
Copy code
@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    @Inject
    lateinit var saveUserRepository: SaveUserRepositoryImp
    @Inject
    lateinit var socketServiceImp: ChatSocketServiceImp



    val googleSignInUiClient by lazy {
        GoogleSignInUi(
            contxt = applicationContext,
            signInClint = Identity.getSignInClient(applicationContext),
            saveUserRepository
        )
    }

    @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        val viewModelStore = ViewModelStore()
        super.onCreate(savedInstanceState)
        setContent {
            FrontEndTheme {
                // A surface container using the 'background' color from the theme

                val viewmodel = viewModel<SignInViewModel>()
                val navController = rememberNavController()

                // Set the ViewModelStore for the NavController
                navController.setViewModelStore(viewModelStore)

                val launcher = rememberLauncherForActivityResult(
                    contract = ActivityResultContracts.StartIntentSenderForResult(),
                    onResult = { result ->
                        if (result.resultCode == RESULT_OK) {
                            lifecycleScope.launch {
                                //getting result here
                                val signInResult = googleSignInUiClient.signInWithIntent(
                                    intent = result.data ?: return@launch
                                )

                                viewmodel.signInResult(signInResult)
                            }

                        }
                    }
                )

                Log.d("debug", "onCreate: towardRood")
                RootNavigationGraph(navController = navController, googleSignInUi = googleSignInUiClient , onSignInClick = {
                    lifecycleScope.launch {
                        // Set loading status to true before starting the operation
                        viewmodel.loadingStatus.value = LoadingStatus(true)

                        val signInIntent = googleSignInUiClient.signIn()
                        launcher.launch(
                            IntentSenderRequest.Builder(
                                signInIntent ?: return@launch
                            ).build()
                        )
                    }
                })


            }
        }
    }
}
Copy code
RootNavigationGraph                                           
@Composable
fun RootNavigationGraph(navController: NavHostController ,googleSignInUi: GoogleSignInUi , onSignInClick : () -> Unit) {

    val viewmodel = viewModel<SignInViewModel>()
    val state by viewmodel.state.collectAsState()
    Log.d("debug1", "RootNavigationGraph: root level 1")
    //this is root graph
    NavHost(navController = navController, startDestination = Graph.AUTHENTICATION , route = Graph.ROOT  )
    {
        //first nested graph for auth
        authNavGraph(
            navController = navController ,
            onSignInClick = { //when sign in button is clicked
                onSignInClick()
            },
            googleSignInUi = googleSignInUi ,
            state = state
            )

        composable(
            route = Graph.MAIN_SCREEN_PAGE
        ){
            Log.d("debug", "RootNavigationGraph: getting into mainscreen")
            //when user successfully login
             MainScreen(navController , googleSignInUi)
        }

    }

}

object Graph{
    const val ROOT = "root-graph"
    const val AUTHENTICATION = "authentication_graph"
    const val MAIN_SCREEN_PAGE = "main_screen_page_graph"
    const val DETAILS = "detail_graph"

}
Nested authNavGraph
Copy code
fun NavGraphBuilder.authNavGraph(
    navController: NavHostController,
    onSignInClick: () -> Unit,
    googleSignInUi: GoogleSignInUi,
    state: SignInState
) {

    //auth graph nested its route is auth
    navigation(
        route = Graph.AUTHENTICATION,
        startDestination = AuthScreen.SPLASH.route
    ){

        composable(route = AuthScreen.SPLASH.route){
            //splash if success navigate onRouteClick
           splashScreen(googleSignInUi = googleSignInUi , navController = navController )
        }
        composable(route = AuthScreen.SIGNUP.route){
            SignInScreen(state = state , onSignInClick = {onSignInClick()} , navController)
        }

    }


}


sealed class AuthScreen(val route : String)
{
    object SPLASH : AuthScreen("SPLASH")
    object SIGNUP : AuthScreen("SIGNUP")
    object LOGIN : AuthScreen("LOGIN")
}
MainScreen here im displaying bottom bar and im getting error inside surface MainNavGraph(navController = navController , googleSignInUi = googleSignInUi)
Copy code
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(navController : NavHostController = rememberNavController(), googleSignInUi: GoogleSignInUi) {

    Scaffold(
        topBar = {BottomBar(navController)}
    ) {
        Surface(modifier = Modifier.padding(it)) {
            Log.d("debug", "MainScreen: level 7")
            MainNavGraph(navController = navController , googleSignInUi = googleSignInUi)
        }

    }
}

@Composable
fun BottomBar(navController: NavController) {
    Log.d("debug", "MainScreen: level 2")

    val navList = listOf(
        BottomNavigationIcon(
            title = "Chats",
            selectedIcon = R.drawable.baseline_home_24,
            unSelectedIcon = R.drawable.outline_home_24,
            hasNews = false,
            badgeCounter = null,
            route = Routes.Chat_Screen
        ),
        BottomNavigationIcon(
            title = "FindOne",
            selectedIcon = R.drawable.baseline_explore_24,
            unSelectedIcon = R.drawable.outline_explore_24,
            hasNews = false,
            route = Routes.Search_Screen
        ),
        BottomNavigationIcon(
            title = "Profile",
            selectedIcon = R.drawable.baseline_profile_24,
            unSelectedIcon = R.drawable.outline_person_24,
            hasNews = false,
            route = Routes.Profile_Screen
        )
    )

    val navBackStackEntry1 by navController.currentBackStackEntryAsState()


    Log.d("debug", "MainScreen: level 3")

    val navBackStackEntry by navController.currentBackStackEntryAsState()

    Log.d("debug", "MainScreen: level 4")

    Log.d("debug", "BottomBar: ${navBackStackEntry != null}")


    Log.d("debug", "MainScreen: level 5")
    val currentDestination = navBackStackEntry?.destination
    Log.d("debug", "MainScreen: level 6")


    val bottomDestination = navList.all { it.route  == currentDestination?.route }

    if (bottomDestination){
        NavigationBar {
            navList.forEach{screen ->
                AddItems(
                    screen ,
                    currentDestination,
                    navController
                )

            }
        }
    }



}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddItems(screen: BottomNavigationIcon, currentDestination: NavDestination?, navController: NavController) {
    NavigationBar {
            NavigationBarItem(
                label = { Text(text = screen.title.toString()) },
                selected =  currentDestination?.hierarchy?.any { it.route == screen.route } == true,
                onClick = {
                    navController.navigate(screen.route){
                        popUpTo(navController.graph.findStartDestination().id)
                        launchSingleTop = true
                    }
                },
                icon = {
                    BadgedBox(
                        badge ={
                            if(screen.badgeCounter != null)
                            {
                                Badge {
                                    Text(text = screen.badgeCounter.toString())
                                }
                            }else if(screen.hasNews){
                                Badge()
                            }
                        }) {
                        Icon(
                            painter = painterResource(screen.selectedIcon),
                            contentDescription = null
                        )
                    }
                })

    }
}

data class BottomNavigationIcon(
    val title : String,
    val selectedIcon : Int,
    val unSelectedIcon : Int,
    val hasNews : Boolean,
    val badgeCounter : Int? = null,
    val route  : String

)
MainNavGraph
Copy code
@Composable
fun MainNavGraph(navController: NavHostController , googleSignInUi: GoogleSignInUi) {

    NavHost(
        navController = navController,
        startDestination = Routes.Chat_Screen,
        route = Graph.MAIN_SCREEN_PAGE
    ){
        Log.d("debug", "MainNavGraph: level 8")

        composable(route = Routes.Chat_Screen){
           // ChatScreen()
        }
        composable(route = Routes.Search_Screen){
           // SearchScreen()
        }
        composable(route = Routes.Profile_Screen){
           // ProfileScreen(googleSignInUi = googleSignInUi)
        }

    }
}
i
Copy code
val viewModelStore = ViewModelStore()
is never going to work. Nor is setting the ViewModelStore on every recomposition. Just remove those lines, that code is only breaking things
h
Then what should I do on stack overflow they said to create two different NavHost with their different instances
i
You shouldn't do that either 95% of the time. Maybe better to explain what you are trying to do in the first place
h
I'm creating a chat application , in this in have splash screen , login with Google this screen require googleSignInClint and context I defined that in a mainActivity and passed the instance down to rootNavGraph , after successful login or already logged in the mainScreen appear which has a scafold for bottom bar (chat , search and profile)
In this code what I did is I have RootNavigaionGraph which hai two nested graphs one is for (AuthNaveGraph) and one is for (mainScreen Scaffold one ) that is Composable () On (AuthNavGraph) im again having two nested graphs one for splash screen and one simple composable () for loginscreen If u logged in or login is successful For that I have (mainNavGraph) that is NavHost And that graph has three composable() for (chat screen , search screen, profile screen) My app is crashing when I reach to (mainNavGraph) the Navhost one nested inside RootGraph
i
login with Google this screen require googleSignInClint and context I defined that in a mainActivity and passed the instance down to rootNavGraph
You can do all of the login behavior in Compose, there's no reason you should need to do any of this in your activity. You might consider looking at this SO post for instance
in this in have splash screen
You might also look at this thread about how you should be doing splash screens (it is with the Splash Screen API, not a destination in your graph
and one is for (mainScreen Scaffold one ) that is Composable ()
And we had talked about how you should be showing/hiding shared UI like a bottom bar based on what screen you are on in this thread, as well as the fact that you don't need multiple NavHosts for this
h
Thanks you Sir for helping : D graph is working now
I moved my all code from the main activity to signup , MainActivity is only holding single component ie RootGraph , but i have two issues , first my signup redriection functionaly is not working , I have a viewModel assoceated with my signup screen where i have a
STATE
for success or error
Copy code
private val _state = MutableStateFlow(SignInState())
val state = _state.asStateFlow()
im updating my state on success
Copy code
fun signInResult(result : SignInResult)
{
    Log.d("debug", "signInResult: is result not equal null  ${result.data != null} ")
    _state.update {
        it.copy(
            isSuccessFull = result.data != null,
            errorMessage = result.errorMessage
        )
    }
}
and inside my
signUp
screen i have a
LaunchEffect
that triggers when state is change and navigate to mainscreen but in my case i moved my code from main to sigin ,now its not triggring
LaunchEffect
Copy code
LaunchedEffect(key1 = state.isSuccessFull) {
    Log.d("debug", "Success ${viewModel.state.value.isSuccessFull}")
    if (state.isSuccessFull) {
        // Set loading status to false when the operation completes
        // viewModel.loadingStatus.value = LoadingStatus(false)
        //ifLogin Successful
        navController.navigate(Graph.MAIN_SCREEN_PAGE)
        //viewModel.resetState()
    }
}
so to navigate i simply pass the navcontroler to viewmodel but i dont know its a right approch or not manging resultis in their own screen
Copy code
fun signInResult(result : SignInResult , navController: NavHostController)
{
    //code for state update
    if (state.value.isSuccessFull){
        Log.d("debug", "signInResult: Login")
        navController.navigate(Graph.MAIN_SCREEN_PAGE){
            popUpTo(AuthScreen.SIGNUP.route){
                inclusive = true
            }
        }
        resetState()
    }
}