如何在Jetpack Compose的Activity的onStart中实现页面导航?
解决Jetpack Compose中已登录用户自动导航至首页的问题
在Compose的单Activity架构下,无需再用StartActivity()跳转页面,而是通过Navigation Compose导航组件配合状态管理实现页面切换。核心思路是让登录状态成为可观察的数据源,导航组件根据状态自动完成跳转。
步骤1:创建登录状态ViewModel
用ViewModel持有登录状态,确保状态能在Activity和可组合项间共享,且生命周期稳定:
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch class AuthViewModel : ViewModel() { // 私有可变状态,仅在ViewModel内修改 private val _isUserLoggedIn = MutableStateFlow(false) // 公开只读状态,供外部观察 val isUserLoggedIn: StateFlow<Boolean> = _isUserLoggedIn // 更新登录状态 fun updateLoginStatus(isLoggedIn: Boolean) { viewModelScope.launch { _isUserLoggedIn.emit(isLoggedIn) } } }
步骤2:修改MainActivity集成ViewModel
在Activity中初始化ViewModel,在onStart检查登录状态并更新ViewModel,同时在登录成功后同步状态:
class MainActivity : ComponentActivity() { lateinit var mGoogleSignInClient: GoogleSignInClient private lateinit var signInLauncher: ActivityResultLauncher<Intent> // 初始化AuthViewModel private val authViewModel by viewModels<AuthViewModel>() override fun onStart() { super.onStart() val account = GoogleSignIn.getLastSignedInAccount(this) // 更新ViewModel的登录状态 authViewModel.updateLoginStatus(account != null) account?.email?.let { Log.d("Signed In: ", it) } } override fun onCreate(savedInstanceState: Bundle?) { val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestEmail() .build() mGoogleSignInClient = GoogleSignIn.getClient(this, gso) signInLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> val task = GoogleSignIn.getSignedInAccountFromIntent(result.data) handleSignInResult(task) } super.onCreate(savedInstanceState) setContent { // 将ViewModel传递给导航组件 Navigation( authViewModel = authViewModel, mGoogleSignInClient = mGoogleSignInClient, signInLauncher = signInLauncher ) } } private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) { try { val account = completedTask.getResult(ApiException::class.java) // 登录成功后更新状态 authViewModel.updateLoginStatus(true) val idToken = account.idToken } catch (e: ApiException) { Log.w("Sign In Error", "signInResult:failed code=" + e.statusCode) Toast.makeText(this, "Sign in failed", Toast.LENGTH_SHORT).show() } } }
步骤3:完善Navigation组件,根据状态自动导航
先定义路由:
sealed class Screens(val route: String) { object LoginScreen : Screens("login_screen") object UserHomeScreen : Screens("user_home_screen") }
再编写导航可组合项,监听登录状态变化自动跳转:
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController @Composable fun Navigation( authViewModel: AuthViewModel, mGoogleSignInClient: GoogleSignInClient, signInLauncher: ActivityResultLauncher<Intent> ) { val navController = rememberNavController() val isLoggedIn = authViewModel.isUserLoggedIn.collectAsState() // 监听登录状态变化,自动触发导航 LaunchedEffect(isLoggedIn.value) { if (isLoggedIn.value) { // 跳转到首页并清空返回栈,防止返回登录页 navController.navigate(Screens.UserHomeScreen.route) { popUpTo(Screens.LoginScreen.route) { inclusive = true } } } else { // 未登录时跳转到登录页 navController.navigate(Screens.LoginScreen.route) { popUpTo(Screens.UserHomeScreen.route) { inclusive = true } } } } NavHost(navController = navController, startDestination = Screens.LoginScreen.route) { composable(Screens.LoginScreen.route) { LoginScreen( mGoogleSignInClient = mGoogleSignInClient, signInLauncher = signInLauncher ) } composable(Screens.UserHomeScreen.route) { UserHomeScreen() } } } // 示例登录页面 @Composable fun LoginScreen( mGoogleSignInClient: GoogleSignInClient, signInLauncher: ActivityResultLauncher<Intent> ) { Button(onClick = { val signInIntent = mGoogleSignInClient.signInIntent signInLauncher.launch(signInIntent) }) { Text(text = "Google登录") } } // 示例首页 @Composable fun UserHomeScreen() { Text(text = "欢迎来到首页!") }
关键说明
- 状态管理:用ViewModel的StateFlow持有登录状态,确保配置变化(如屏幕旋转)时状态不丢失,且可被可组合项观察。
- 导航触发:
LaunchedEffect会在isLoggedIn值变化时执行,自动完成导航,避免在Activity中直接操作导航控制器。 - 返回栈处理:
popUpTo配合inclusive = true,确保跳转后无法返回之前的页面,符合登录后的预期行为。
内容的提问来源于stack exchange,提问作者Dhanush Raja
相关产品推荐
相关产品推荐

