如何使用 Compose Multiplatform 实现 Google 登录

问题描述 投票:0回答:1

我是 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

kotlin mobile google-signin kotlin-multiplatform compose-multiplatform
1个回答
0
投票

您正在尝试将您的 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)
)
© www.soinside.com 2019 - 2024. All rights reserved.