如何将Google登录逻辑迁移至Jetpack Compose的LoginScreen并集成导航?
解决方案:将Google登录逻辑迁移至Jetpack Compose可组合项并集成导航
核心思路
把登录业务逻辑抽离到ViewModel中,使用Compose专属的rememberLauncherForActivityResult替代Activity级别的registerForActivityResult,通过Navigation组件实现页面跳转,彻底解耦UI与业务逻辑。
步骤1:创建LoginViewModel处理业务逻辑
ViewModel负责管理登录状态、处理Google登录/登出逻辑、持有GoogleSignInClient,避免直接在可组合项中依赖Activity上下文。
import android.app.Application import android.widget.Toast import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInClient import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.common.api.ApiException import kotlinx.coroutines.launch class LoginViewModel(application: Application) : AndroidViewModel(application) { // 维护当前用户名状态 val username = MutableLiveData<String>("anonymous") private val appContext = getApplication<Application>().applicationContext // 初始化GoogleSignInClient val googleSignInClient: GoogleSignInClient by lazy { val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestEmail() .build() GoogleSignIn.getClient(appContext, gso) } // 处理登录结果 fun handleSignInResult(task: com.google.android.gms.tasks.Task<com.google.android.gms.auth.api.signin.GoogleSignInAccount>) { viewModelScope.launch { try { val account = task.getResult(ApiException::class.java) username.postValue(account.displayName ?: "未知用户") Toast.makeText(appContext, "登录成功", Toast.LENGTH_SHORT).show() } catch (e: ApiException) { Toast.makeText(appContext, "登录失败:${e.statusCode}", Toast.LENGTH_SHORT).show() username.postValue("anonymous") } } } // 执行登出操作 fun signOut() { googleSignInClient.signOut() .addOnCompleteListener { username.postValue("anonymous") Toast.makeText(appContext, "登出成功", Toast.LENGTH_SHORT).show() } } // 检查当前已登录用户 fun checkCurrentUser() { val account = GoogleSignIn.getLastSignedInAccount(appContext) account?.let { username.postValue(it.displayName ?: "未知用户") } ?: run { username.postValue("anonymous") } } }
步骤2:编写LoginScreen可组合项
在可组合项中使用rememberLauncherForActivityResult注册登录回调,接收NavController实现导航,仅负责UI渲染和触发操作。
import androidx.compose.foundation.layout.* import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.google.android.gms.auth.api.signin.GoogleSignIn import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @Composable fun LoginScreen(navController: NavController, viewModel: LoginViewModel = viewModel()) { val username by viewModel.username.collectAsState() val context = LocalContext.current // 在Compose中注册Activity结果回调 val signInLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> val task = GoogleSignIn.getSignedInAccountFromIntent(result.data) viewModel.handleSignInResult(task) // 登录成功后跳转到主页,清除登录页栈 if (GoogleSignIn.getLastSignedInAccount(context) != null) { navController.navigate("/home") { popUpTo("/login") { inclusive = true } } } } Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { // Google登录按钮 Button( onClick = { val signInIntent = viewModel.googleSignInClient.signInIntent signInLauncher.launch(signInIntent) }, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 10.dp), colors = ButtonDefaults.buttonColors( containerColor = Color.Black, contentColor = Color.White ) ) { Image( painter = painterResource(id = R.drawable.icons8_google), contentDescription = "Google图标" ) Text(text = "使用Google登录", modifier = Modifier.padding(start = 6.dp)) } // 登出按钮 Button( onClick = { viewModel.signOut() }, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), colors = ButtonDefaults.buttonColors( containerColor = Color.Black, contentColor = Color.White ) ) { Image( painter = painterResource(id = R.drawable.icons8_google), contentDescription = "Google图标" ) Text(text = "登出", modifier = Modifier.padding(start = 6.dp)) } Spacer(modifier = Modifier.height(24.dp)) Text(text = "当前用户:$username", fontSize = 30.sp) } }
步骤3:修改MainActivity集成Navigation
将Activity简化为导航容器,初始化NavHost并定义页面路由。
import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MeauComposeTheme { val navController = rememberNavController() NavHost(navController = navController, startDestination = "/login") { // 登录页路由 composable("/login") { LoginScreen(navController = navController) } // 主页路由 composable("/home") { HomeScreen() } } } } } override fun onStart() { super.onStart() // 检查当前已登录用户状态 val viewModel = viewModel<LoginViewModel>() viewModel.checkCurrentUser() } } // 示例主页可组合项 @Composable fun HomeScreen() { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text(text = "欢迎来到主页!", fontSize = 30.sp) } }
关键注意事项
- 依赖配置:确保项目中已添加Compose Navigation和ViewModel依赖:
implementation "androidx.navigation:navigation-compose:2.7.6" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2" - 上下文安全:ViewModel使用Application Context避免内存泄漏,Compose中通过
LocalContext.current获取临时上下文。 - 路由管理:使用
popUpTo清除登录页栈,防止用户通过返回键回到登录页。
内容的提问来源于stack exchange,提问作者Dhanush Raja
相关产品推荐
相关产品推荐

