Hey guys. Have a few more questions on compose na...
# compose
d
Hey guys. Have a few more questions on compose navigation. Im using the navigation component. How are you guys handling global states like logged in and logged out? Before I was handling like the following
Copy code
class MyAppModel: ViewModel{
    val loggedInState: Flow<Boolean>
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        "SETUP APP MODEL HERE"
        val myAppModel= MyAppModel()
        setContent {
            MyUI(myAppModel)
        }
    }
}

@Composable
fun MyUI(vm: MyAppModel){
    val loggedIn = vm.loggedInState.asLiveData().observeAsState()
    when (loggedIn.value){
        is true -> DasboardUI()
        is false -> AuthUI()
    }
}
Now that I have a navgraph. It seems like Ill have to have to observe this state in every path in the navgraph to be able to react to it? Otherwise I could create two separate navigation hosts and have them separate for Authentication workflow and the rest of the app?
1
i
We talked a lot about login with Navigation in this previous video:

https://www.youtube.com/watch?v=09qjn706ITA&amp;t=273s

Specifically, when it comes to web URLs, process restoration, and deep linking into content, you need to check that state in each destination that requires login (as it could be the very first destination to actually be ran). That's the screen that needs to kick off the login flow, thus returning the user to exactly the place that they were, with all of their state in tact, once they've finished logging in
The same concepts discussed in that video and in the documentation specifically around login (https://developer.android.com/guide/navigation/navigation-conditional#login0 also apply to Navigation Compose
c
Ian, what if you're going the Single Activity with multiple composable screens route?
you need to check that state in each destination that requires login (as it could be the very first destination to actually be ran).
Couldn't you basically just have this
isUserLoggedIn
state on the application or activity level, and then if that state updates, then the activity just uses
navigator
to open up the login screen?
d
@Colton Idle that was what I was thinking.
But would we know the navigationcontroller is in the same context to avoid issues?
c
Yeah. I guess I always thought that with Single Activity and pure composable screens that the Activity could be the source of truth and coordinate everything.
i
I think you need to talk to your designers about what your UX (user experience) should be. Take every screen in your app and ask "what should happen if the user is logged out while on this screen?" Even traditionally "login required" types of apps (i.e., Twitter, Facebook, etc) don't treat every screen the same - you should still be able to view a public tweet while logged out. This means the login decision is deferred - a button in that screen and not a forced move to the login
As soon as you get even one exception, then you really do want the handling at the destination level, which, if you were following along, was the recommendation from the beginning and avoids you having to rewrite anything or caveat approaches that weren't flexible and weren't scalable in the first place
c
Interesting. When you put it that way it makes sense. I guess just in the spirit of DRY I wanted to see if I could prevent some of that duplicate code.
Also, I'm still grokking state and single source of truth and so this "seemed" like a no brainer of "keep the state in one place and it controls all events"
@Ian Lake Based off of what we talked about above, this is basically the simplest example I could throw together. Does this seem like a reasonable way that a team could handle logged in/out with Compose?
Copy code
@Composable
fun HomeScreen(
    navController: NavController,
    screenTitle: String,
    viewModel: HomeScreenViewModel = viewModel()
) {
    if (viewModel.isLoggedIn()) {
        HomePageContent(navController = navController, screenTitle = screenTitle)
    } else {
        // go to sign up screen
    }
}