Skip to content

Instantly share code, notes, and snippets.

@AndroidPoet
Created April 17, 2026 17:28
Show Gist options
  • Select an option

  • Save AndroidPoet/290e7c2013c2c8ecde91af577dc063c2 to your computer and use it in GitHub Desktop.

Select an option

Save AndroidPoet/290e7c2013c2c8ecde91af577dc063c2 to your computer and use it in GitHub Desktop.
sealed class AuthState {
data object Loading : AuthState()
data object Unauthenticated : AuthState()
data class Authenticated(val userId: String, val onboardingComplete: Boolean) : AuthState()
}
sealed class RootRoute {
data object Splash : RootRoute()
data object Login : RootRoute()
data object Signup : RootRoute()
data object Onboarding : RootRoute()
data object Main : RootRoute()
}
sealed class MainRoute {
data object Home : MainRoute()
data object Settings : MainRoute()
}
@Composable
fun RootNavigation(authViewModel: AuthViewModel) {
val authState by authViewModel.authState.collectAsState()
val navController = remember { NavController(RootRoute.Splash) }
NavHost(navController, RootRoute.Splash) {
composable<RootRoute.Splash> {
SplashScreen()
LaunchedEffect(authState) {
val destination = when (authState) {
AuthState.Loading -> {
return@LaunchedEffect
}
AuthState.Unauthenticated -> RootRoute.Login
is AuthState.Authenticated -> {
val auth = authState as AuthState.Authenticated
if (auth.onboardingComplete) {
RootRoute.Main
} else {
RootRoute.Onboarding
}
}
}
navController.navigate(destination) {
popUpTo(RootRoute.Splash) { inclusive = true }
}
}
}
composable<RootRoute.Login> {
LoginScreen(
onLoginSuccess = {
navController.navigate(RootRoute.Onboarding) {
popUpTo(RootRoute.Login) { inclusive = true }
}
},
onSignupClick = {
navController.navigate(RootRoute.Signup)
}
)
}
composable<RootRoute.Signup> {
SignupScreen(
onSignupSuccess = {
navController.navigate(RootRoute.Onboarding) {
popUpTo(RootRoute.Login) { inclusive = true }
}
},
onBackClick = {
navController.popBackStack()
}
)
}
composable<RootRoute.Onboarding> {
OnboardingScreen(
onOnboardingComplete = {
navController.navigate(RootRoute.Main) {
popUpTo(RootRoute.Onboarding) { inclusive = true }
}
}
)
}
navigation<RootRoute.Main, MainRoute>(
startDestination = MainRoute.Home
) {
composable<MainRoute.Home> {
HomeScreen()
}
composable<MainRoute.Settings> {
SettingsScreen(
onLogout = {
navController.navigate(RootRoute.Login) {
popUpTo(RootRoute.Main) { inclusive = true }
}
}
)
}
}
}
}
@Composable
fun LoginScreen(
onLoginSuccess: () -> Unit,
onSignupClick: () -> Unit
) {
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var isLoading by remember { mutableStateOf(false) }
var error by remember { mutableStateOf<String?>(null) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center
) {
TextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
if (error != null) {
Text(error!!, color = Color.Red, modifier = Modifier.padding(8.dp))
}
Button(
onClick = {
isLoading = true
},
modifier = Modifier.fillMaxWidth(),
enabled = !isLoading
) {
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.size(20.dp))
} else {
Text("Login")
}
}
Spacer(modifier = Modifier.height(16.dp))
TextButton(onClick = onSignupClick) {
Text("Don't have an account? Sign up")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment