为什么我无法在 Jetpack Compose 中使用 Firebase 的移动身份验证?

Why I am not able to use mobile authentication with firebase in jetpack compose?

我正在尝试学习 android jetpack compose,我有一个简单的应用程序,我想在我的项目中使用带有 firebase 的移动身份验证。我在项目中使用了MVVM,所以当我调试项目时,它会抛出类似

的错误

You must specify an Activity on your PhoneAuthOptions. Please call #setActivity()

对于视图模型中的.build(),我该如何安排我的视图模型,我在网上没有找到任何解决方案,有什么想法吗?

导航:

@OptIn(ExperimentalPagerApi::class)
@Composable
fun NavScreen(

) {


val modelAuthentication = 
hiltViewModel<AuthenticationViewModel>.()

val navController = rememberNavController()

NotificationMessage(viewModel = viewModel())

 NavHost(navController = navController, startDestination = 
"phone") {
composable("phone") {
        PhoneScreen(navController = navController, 
 modelAuthentication = modelAuthentication) { _, _ ->

        }

    }

    composable("phoneVerify") {
        PhoneVerifyScreen(navController = navController, 
  modelAuthentication = modelAuthentication) { _, _ ->

        }

    }
  }}

视图模型:

@HiltViewModel
class AuthenticationViewModel @Inject constructor(


    ) : ViewModel() {

    private val mAuth = FirebaseAuth.getInstance()

    var verificationOtp = ""
    var popNotification = mutableStateOf<Event<String>?>(null)

 private lateinit var baseBuilder: PhoneAuthOptions.Builder

fun setActivity(activity: Activity) {
    baseBuilder = 
PhoneAuthOptions.newBuilder().setActivity(activity)

}



    fun send(mobileNum: String) {
        val options = baseBuilder
            .setPhoneNumber("+91$mobileNum")
            .setTimeout(60L, TimeUnit.SECONDS)
            .setCallbacks(object :
                PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
                override fun onVerificationCompleted(p0: PhoneAuthCredential) {
                    handledException(customMessage = "Verification Completed")

                }

                override fun onVerificationFailed(p0: FirebaseException) {
                    handledException(customMessage = "Verification Failed")

                }

                override fun onCodeSent(otp: String, p1: PhoneAuthProvider.ForceResendingToken) {
                    super.onCodeSent(otp, p1)
                    verificationOtp = otp
                    handledException(customMessage = "Otp Send Successfully")

                }
            }).build()
        PhoneAuthProvider.verifyPhoneNumber(options)
    }

     fun otpVerification(otp: String) {
        val credential = PhoneAuthProvider.getCredential(verificationOtp, otp)
        FirebaseAuth.getInstance().signInWithCredential(credential)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    handledException(customMessage = "Verification Successful")

                } else {
                    handledException(customMessage =  "Wrong Otp")

                }
            }
    }


    private fun handledException(exception: Exception? = null, customMessage: String = "") {
        exception?.printStackTrace()
        val errorMsg = exception?.message ?: ""
        val message = if (customMessage.isEmpty()) {
            errorMsg
        } else {
            "$customMessage: $errorMsg"
        }
        popNotification.value = Event(message)
    }


    }

屏幕1:

@Composable
fun PhoneScreen(
    navController: NavController,
    modelAuthentication: AuthenticationViewModel,
    onClick: (mobileNum: String, otp: String) -> Unit
) {

    val phoneNumber = remember { mutableStateOf("") }

  val context = LocalContext.current

   LaunchedEffect(Unit) {

    println("found activity? ${context.findActivity()}")
    val activity = context.findActivity() ?: 
    return@LaunchedEffect
    modelAuthentication.setActivity(activity)

    }


   OutlinedTextField(
            value = phoneNumber.value,
            colors = TextFieldDefaults.textFieldColors(
                backgroundColor = white,
                focusedIndicatorColor = Grey,
                unfocusedIndicatorColor = Grey,
                focusedLabelColor = Grey,
                unfocusedLabelColor = Grey,
            

                ),
            onValueChange = { phoneNumber.value = it },
            label = { Text(text = "Phone Number") },
            placeholder = { Text(text = "Phone Number") },
            singleLine = true,
            modifier = Modifier.fillMaxWidth(0.8f)

        )

    Button(
                modifier = Modifier
                    .width(285.dp)
                    .height(55.dp),
                onClick = {

                    modelAuthentication.send(phoneNumber.value)
                    navController.navigate("phoneVerify")

                },
                colors = ButtonDefaults.buttonColors(
                    backgroundColor = Color.Custom
                ),
                shape = RoundedCornerShape(60)
            ) {
                Text(
                    text = "Next",
                    style = TextStyle(
                        fontSize = 18.sp,
                        color = white,


                        )

                )
            }
}

屏幕2:

@Composable
fun PhoneVerifyScreen(
navController: NavController,
modelAuthentication: AuthenticationViewModel,
onClick: (mobileNum: String, otp: String) -> Unit
 ) {

 val phoneNumberOtp = remember { mutableStateOf("") }

 val context = LocalContext.current

   LaunchedEffect(Unit) {

    println("found activity? ${context.findActivity()}")

    val activity = context.findActivity() ?: 
    return@LaunchedEffect
    modelAuthentication.setActivity(activity)

    }

   OutlinedTextField(
            value = phoneNumberOtp.value,
            colors = TextFieldDefaults.textFieldColors(
                backgroundColor = white,
                focusedIndicatorColor = Grey,
                unfocusedIndicatorColor = Grey,
                focusedLabelColor = Grey,
                unfocusedLabelColor = Grey,
            

                ),
            onValueChange = { phoneNumberOtp.value = it },
            label = { Text(text = "code") },
            placeholder = { Text(text = "code") },
            singleLine = true,
            modifier = Modifier.fillMaxWidth(0.8f)

        )

    Button(
                modifier = Modifier
                    .width(285.dp)
                    .height(55.dp),
                onClick = {

                modelAuthentication.otpVerification(phoneNumberOtp.value)

                navController.navigate("home")

                },
                colors = ButtonDefaults.buttonColors(
                    backgroundColor = Color.Custom
                ),
                shape = RoundedCornerShape(60)
            ) {
                Text(
                    text = "Next",
                    style = TextStyle(
                        fontSize = 18.sp,
                        color = white,


                        )

                )
            }
}

您需要 specify Activity 因为 它是构建器的必需参数。

您必须将构建器的代码移动到您的 Activity,因为您不允许在 ViewModel 中使用 link 到 Activity。

在验证结果回调中可以调用ViewModel的方法传递结果或异常。

我很确定应该可以使用 Hilt 注入 activity 上下文,并希望看到@Alex Mamo 或其他人的解决方案。

现在这里是 hacky 解决方案:

@HiltViewModel
class AuthenticationViewModel @Inject constructor(
) : ViewModel() {
    private lateinit var baseBuilder: PhoneAuthOptions.Builder

    fun setActivity(activity: Activity) {
        baseBuilder = PhoneAuthOptions.newBuilder().setActivity(activity)
    }

    fun send(mobileNum: String) {
        val options = baseBuilder
            //.setPhoneNumber...
    }
}

在您看来:

val viewModel = viewModel<AuthenticationViewModel>()
val context = LocalContext.current
LaunchedEffect(Unit) {
    val activity = context.findActivity() ?: return@LaunchedEffect
    viewModel.setActivity(activity)
}

findActivity:

fun Context.findActivity(): Activity? = when (this) {
    is Activity -> this
    is ContextWrapper -> baseContext.findActivity()
    else -> null
}