Jetpack Compose报错:@Composable调用仅能在Composable上下文执行(谷歌登录)
解决Android Jetpack Compose谷歌登录中的
@Composable invocations can only happen from the context of a @Composable function错误 错误原因
googleSignIn被标记为@Composable函数,但在OutlinedButton的onClick回调中调用它——onClick的lambda属于非Composable上下文,不允许调用Composable函数。rememberLauncherForActivityResult必须在Composable作用域内初始化,但启动登录Intent的操作可以在普通lambda中执行。
解决方案
拆分谷歌登录的职责,将Composable相关的初始化逻辑放在正确的作用域内,把登录逻辑和结果处理封装为普通函数,确保context能正常传递使用:
修改后的loginButton函数
@Composable fun loginButton(loginType: Int, context: Context, googleSignInClient: GoogleSignInClient?) { // 在Composable作用域内初始化ActivityResultLauncher val signInLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> handleGoogleSignInResult(result, context) } Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth()) { if (loginType == 0) { OutlinedButton( onClick = { // 调用普通函数启动谷歌登录,避免在非Composable上下文调用Composable googleSignInLaunch(signInLauncher, googleSignInClient) }, border = BorderStroke(2.dp, Color.Black), colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFFFFFFFF)), modifier = Modifier .height(40.dp) .width(300.dp), shape = RoundedCornerShape(50) ) { Image( painter = painterResource(id = R.drawable.google_logo), contentDescription = "Google Logo(Src: Wikipedia)" ) Spacer(modifier = Modifier.width(30.dp)) Text(text = "Sign in with Google", fontSize = 15.sp, color = Color.Black) } } else if (loginType == 1) { OutlinedButton( onClick = {}, border = BorderStroke(2.dp, Color.Black), colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFFFFFFFF)), modifier = Modifier .height(40.dp) .width(300.dp), shape = RoundedCornerShape(50) ) { Image( painter = painterResource(id = R.drawable.apple_logo), contentDescription = "Apple Logo" ) Spacer(modifier = Modifier.width(30.dp)) Text(text = "Sign in with Apple", fontSize = 15.sp, color = Color.Black) } } else { Text("Cannot load the sign in button") } } }
拆分后的谷歌登录逻辑函数(独立Kotlin文件)
import android.app.Activity import android.content.Context import android.content.Intent import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInAccount import com.google.android.gms.common.api.ApiException import com.google.android.gms.tasks.Task import android.util.Log import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher // 处理谷歌登录结果,可直接使用传入的context fun handleGoogleSignInResult(result: ActivityResult, context: Context) { if (result.resultCode == Activity.RESULT_OK) { val intent = result.data intent?.let { val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(it) try { val account = task.getResult(ApiException::class.java) Log.d("AuthManager", "FirebaseAuthWithGoogle" + account.id) // 此处可使用context执行后续操作,如跳转页面、显示Toast等 } catch (e: ApiException) { e.printStackTrace() Log.w("LoginActivity", "Google sign in failed", e) } } } } // 启动谷歌登录Intent的普通函数 fun googleSignInLaunch(launcher: ActivityResultLauncher<Intent>, googleSignInClient: GoogleSignInClient?) { googleSignInClient?.signInIntent?.let { launcher.launch(it) } }
修改说明
- 将
rememberLauncherForActivityResult移到loginButton内部,确保它在Composable作用域中初始化,符合Jetpack Compose的规则。 - 把原
googleSignIn中的结果处理逻辑拆分为普通函数handleGoogleSignInResult,直接接收context参数,可正常使用上下文做后续操作。 - 新增
googleSignInLaunch函数负责启动登录Intent,在onClick的lambda中直接调用这个普通函数,规避了在非Composable上下文调用Composable函数的问题。
内容的提问来源于stack exchange,提问作者Jalim
相关产品推荐
相关产品推荐

