Trying to learn Compose. My goal is to have one ac...
# compose
n
Trying to learn Compose. My goal is to have one activity and no xml/layouts. Only work with composables as views and using one loginscreen and a bottom navigationbar that reuse/saves state of its viewmodels scooped to the views. I need som help and input on how to think about this navigation setup. Code in my comment.
Is this correct way of scooping a viewmodel to each composable screens lifecyclescoop inside a navigationhost? I get strange behavior when using this cycle and observing clickCountHome inside HomeViewModel Cycle: Login(press login) -> Home(clickCount set to 1) -> Profile(Press Logout) -> Login(press login) -> Home(clickCount is 0) -> Profile(no action) ->Home(clickCount is 1) Why does clickCount first show 0 on login and then 1. Seems to me i have two HomeScreens in my backstack with different scooped viewmodels. How can i check what exists inside my backstack? MainViewComposable:
Copy code
@Composable
fun MainApp(mView: MapView) {
    val mainViewModel: MainViewModel = viewModel()
    val navController = rememberNavController()
    val scaffoldState = rememberScaffoldState()
    val isLoggedIn = mainViewModel.isLoggedIn.value
    val bottomBar: @Composable () -> Unit = {
            BottomBar(
                navController = navController,
                screens = screensFromBottomNav
            )
    }

    Scaffold(
        bottomBar =  {
            if(isLoggedIn) { bottomBar() }
        },
        scaffoldState = scaffoldState,
    ) {
        Box(modifier = Modifier
            .padding(0.dp,it.calculateTopPadding(), 0.dp, it.calculateBottomPadding())
        ){
            NavigationHost(
                navController = navController,
                mainViewModel = mainViewModel,
                mView = mView
            )
        }

    }
}

@Composable
fun NavigationHost(
    navController: NavController,
    mainViewModel: MainViewModel,
    mView: MapView)
{

    NavHost(
        navController = navController as NavHostController,
        startDestination = Screen.LoginScreen.Login.route,//Screen.Nav.Home.route,

    ) {
        composable(Screen.Nav.Home.route) {
            val homeViewModel: HomeViewModel = viewModel()
            mainViewModel.currentScreen.value = Screen.Nav.Home
            HomeScreen(mainViewModel = mainViewModel, homeViewModel = homeViewModel)
        }
        composable(Screen.Nav.Map.route) {
            val mapViewModel: MapViewModel = viewModel()
            mainViewModel.currentScreen.value = Screen.Nav.Map
            MapScreen(mainViewModel = mainViewModel, mapViewModel = mapViewModel, mView = mView)
        }
        composable(Screen.Nav.Profile.route) {
            val profileViewModel: ProfileViewModel = viewModel()
            mainViewModel.currentScreen.value = Screen.Nav.Profile
            ProfileScreen(mainViewModel = mainViewModel, profileViewModel = profileViewModel, navController = navController)
        }
        composable(Screen.LoginScreen.Login.route){
            val loginViewModel: LoginViewModel = viewModel()
            mainViewModel.currentScreen.value = Screen.LoginScreen.Login
            LoginScreen(mainViewModel = mainViewModel, loginViewModel = loginViewModel, navController = navController)
        }
    }
}
Copy code
@Composable
fun BottomBar(modifier: Modifier = Modifier, screens: List<Screen.Nav>, navController: NavController) {
    BottomNavigation(modifier = modifier) {
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination?.route
        screens.forEach { screen ->
            BottomNavigationItem(
                icon = { Icon(imageVector = screen.icon, contentDescription = "") },
                label = { Text(screen.title) },
                selected = currentRoute == screen.route,
                onClick = {
                    navController.navigate(screen.route) {
                        popUpTo(navController.graph.findStartDestination().id) {
                            saveState = true
                        }
                        launchSingleTop = true
                        restoreState = true

                    }
                }
            )
        }
    }
}

sealed class Screen(val route: String, val title: String) {

    sealed class Nav(
        route: String,
        title: String,
        val icon: ImageVector
    ) : Screen(route, title) {
        object Home : Nav("home", "Home", Icons.Filled.Home)
        object Map : Nav("map", "Map", Icons.Filled.Place)
        object Profile : Nav("profile", "Profile", Icons.Filled.Person)
    }

    sealed class LoginScreen(
        route: String,
        title: String
    ) : Screen(route, title) {
        object Login : LoginScreen("login", "Login")
    }
}

val screensFromBottomNav = listOf(
    Screen.Nav.Home,
    Screen.Nav.Map,
    Screen.Nav.Profile
)
Copy code
class MainViewModel : ViewModel() {


    val currentScreen: MutableState<Screen> = mutableStateOf(Screen.LoginScreen.Login)
    val isLoggedIn: MutableState<Boolean> = mutableStateOf(false)

    fun login(navController: NavController){
        isLoggedIn.value = true
        navController.navigate(Screen.Nav.Home.route)
    }

    fun logout(navController: NavController){
        isLoggedIn.value = false
        navController.navigate(Screen.LoginScreen.Login.route)
    }

    override fun onCleared() {
        super.onCleared()

    }
}

class HomeViewModel: ViewModel() {

    var clickCountHome: MutableState<Int> = mutableStateOf(0)

}
i
First of all, as per the Principles of Navigation (https://developer.android.com/guide/navigation/navigation-principles#fixed_start_destination), you should never be using a login destination as your start destination. The guide to conditional navigation also applies here: https://developer.android.com/guide/navigation/navigation-conditional#login
When you say
navigate()
without
popUpTo
you're only adding a new destination to the back stack, which is why your
logout
isn't doing what you think it is doing.
n
Thanks! I have now removed Login from NavHost, put Home as startdestination and changed to this.
Copy code
fun login(navController: NavController){
        isLoggedIn.value = true
        navController.popBackStack()
    }

    fun logout(navController: NavController){
        isLoggedIn.value = false
        navController.popBackStack()
    }
Copy code
Scaffold(
        bottomBar =  {
            if(isLoggedIn) { bottomBar() }
        },
        scaffoldState = scaffoldState,
    ) {
        Box(modifier = Modifier
            .padding(0.dp,it.calculateTopPadding(), 0.dp, it.calculateBottomPadding())
        ){
            if (isLoggedIn){
                NavigationHost(
                    navController = navController,
                    mainViewModel = mainViewModel,
                    mView = mView
                )
            }else {
                LoginScreen(mainViewModel = mainViewModel, navController = navController)
            }


        }

    }
It now seems to work.