我是 Compose Multiplatform 的初学者,我正在尝试使用 Compose Multiplatform 实现 Google Sing In (其目标是获取
GoogleSignInAccount
对象,以检索 serverAuthCode
值)。
这是我的用户界面代码:
@Composable
fun MainActivity(colorScheme: ColorScheme, viewModel: MainActivityViewModel) {
val context = getContext()
val launcherManager = getLauncherManager()
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clickable(
onClick = {
viewModel.viewModelScope.launch {
launcherManager?.let {
AndroidGoogleSignInHandler(context, it.getActivityForResult {
}).signIn()
}
}
}
)
.border(width = 1.dp, color = Color.LightGray)
.padding(
start = 12.dp,
end = 16.dp,
top = 12.dp,
bottom = 12.dp
)
.fillMaxWidth()
.height(40.dp)
) {
/*
Icon(
painter = painterResource(id = R.drawable.ic_google_logo),
contentDescription = "Google Login",
tint = Color.Unspecified
)
*/
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "Sign in With Google"
)
Spacer(modifier = Modifier.width(16.dp))
}
}
}
方法
getLauncherManager()
是一个 expect
方法,能够返回(或由于平台不兼容而不能)函数 registerForActivityResult
,看起来像这样
与 AndroidGoogleSignInHandler
和 ActivityResultLauncherManager
:
@Composable
expect fun getLauncherManager(): ActivityResultLauncherManager?
expect class AndroidGoogleSignInHandler(activity: Any?, signInLauncher: Any?) : GoogleSignInHandler {
override fun signIn()
override fun signOut()
}
expect class ActivityResultLauncherManager(activity: Any?) {
fun getActivityForResult(callback: (Boolean) -> Unit): Any?
}
你可以看到我到处都使用“Any”,因为 iOS 没有
ActivityResultLauncher<Intent>
类型
这里是
androidMain中
actual
函数的实现:
actual interface GoogleSignInHandler {
// Add other methods as needed
actual fun signIn()
actual fun signOut()
}
actual class AndroidGoogleSignInHandler actual constructor(val activity: Any?, val signInLauncher: Any?) : GoogleSignInHandler {
private var googleSignInClient: GoogleSignInClient
init {
// Initialize the GoogleSignInClient
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build()
googleSignInClient = GoogleSignIn.getClient(activity as AppCompatActivity, gso)
}
actual override fun signIn() {
if (activity is Context) {
// Start the sign-in process
val signInIntent = googleSignInClient.signInIntent
(signInLauncher as ActivityResultLauncher<Intent>).launch(signInIntent)
} else {
// Handle invalid activity parameter
}
}
actual override fun signOut() {
// Sign out from Google account
googleSignInClient.signOut()
.addOnCompleteListener { /* Handle sign out completion */ }
}
}
@Composable
actual fun getContext(): Any? {
return LocalContext.current
}
actual class ActivityResultLauncherManager actual constructor(val activity: Any?) {
actual fun getActivityForResult(callback: (Boolean) -> Unit): Any? {
return (activity as? AppCompatActivity)!!.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val signInResult = GoogleSignIn.getSignedInAccountFromIntent(result.data)
val isSuccess = signInResult.isSuccessful
callback(isSuccess)
}
}
}
@Composable
actual fun getLauncherManager(): ActivityResultLauncherManager? {
val context = LocalContext.current as? AppCompatActivity
return remember {
ActivityResultLauncherManager(context!!)
}
您可以在我的代码中看到我有一个回调
callback: (Boolean) -> Unit
,参数中包含请求的成功(布尔值),但正如我在帖子顶部所说,目标是获取serverAuthCode
值所以如果我们找到解决方案,我将用“字符串”替换“布尔”;)
因此,在我的 UI 中,当我尝试调用 AndroidGoogleSignInHandler(...).signIn() 时,我收到此错误:
java.lang.IllegalStateException: LifecycleOwner xxx.xxx.xxx.MainActivity@7dabaee is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
所以我认为我最近才实现
registerForActivityResult
,但我不知道如何处理它,因为它是由 expect
和 actual
关键字完成的...
有没有更好的方法来使用 Compose Multiplatform 实现 GoogleSinIn ?我到处搜索,但找不到解决方案,除了 KMM Jetpack Compose,所以没有 Compose Multiplatform
一个解决方案是使用 WebView,带有登录页面和这个 Google 登录按钮,那就没问题了!但如果我可以为 Android AND iOS 本地实现,我就不想处理这个问题。我的代码只处理 Android,但如果你有解决方案......
感谢您的阅读和帮助! :D
您正在尝试将您的 Android 代码转换为通用代码。
但是通用代码不需要知道每个平台如何进行身份验证,它所需要的只是按下按钮时应该调用的回调。
因此您可以将平台实现移至单个可组合项。它看起来像这样:
@Composable
actual fun rememberOnGoogleAuthAction(onTokenReceived: (String) -> Unit): () -> Unit {
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult(),
onResult = { result ->
val idToken = GoogleSignIn.getSignedInAccountFromIntent(result.data)
.getResult(ApiException::class.java)
.idToken!!
onTokenReceived(idToken)
}
)
val context = LocalContext.current
val client = remember {
GoogleSignIn.getClient(
context,
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken("clientId")
.requestProfile()
.build()
)
}
val coroutineScope = rememberCoroutineScope()
return remember {
{
coroutineScope
.launch {
client.signOut().await()
launcher.launch(client.signInIntent)
}
}
}
}
然后在你的通用代码中这样调用它:
.clickable(
onClick = rememberOnGoogleAuthAction(viewModel::onTokenReceived)
)