You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose Android中实现OTP自动获取(自动填充)

在Jetpack Compose中实现OTP自动填充与手动输入功能

1. 配置基础权限

如果依赖系统自动填充框架,无需额外权限;若要主动读取短信获取OTP,需在AndroidManifest.xml中添加:

<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />

2. 实现支持自动填充的OTP输入框

利用Android系统Autofill框架,让输入框自动识别OTP,同时保留手动输入能力:

import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillNode
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalAutofillTree
import androidx.compose.ui.text.input.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType

@Composable
fun OTPTextField() {
    val otpValue = rememberSaveable { mutableStateOf("") }
    val focusRequester = remember { FocusRequester() }
    val autofillNode = remember {
        AutofillNode(
            autofillTypes = listOf(AutofillType.OneTimeCode),
            onFill = { otpValue.value = it }
        )
    }

    LocalAutofillTree.current += autofillNode

    OutlinedTextField(
        value = otpValue.value,
        onValueChange = {
            if (it.length <= 6) otpValue.value = it // 限制6位OTP长度
        },
        label = { Text("验证码") },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
        modifier = Modifier
            .focusRequester(focusRequester)
            .autofill(autofillNode),
        singleLine = true,
        maxLines = 1
    )

    LaunchedEffect(Unit) {
        focusRequester.requestFocus() // 自动获取焦点,方便输入或触发自动填充
    }
}

3. 手动输入逻辑优化

上述代码已天然支持手动输入,可额外添加以下优化:

  • 监听输入长度,当达到固定位数(如6位)时自动触发验证逻辑
  • 添加清除按钮,允许用户快速重置输入内容
  • 输入过程中实时反馈格式正确性

4. 可选:用SMS Retriever API主动获取OTP

无需用户授权即可自动捕获OTP,需集成Google SMS Retriever API:

步骤1:添加依赖

在Module级build.gradle中加入:

implementation "com.google.android.gms:play-services-auth:20.7.0"
implementation "com.google.android.gms:play-services-auth-api-phone:18.0.1"

步骤2:实现短信接收器

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status

class SMSReceiver : BroadcastReceiver() {
    private var otpCallback: ((String) -> Unit)? = null

    fun setOtpCallback(callback: (String) -> Unit) {
        otpCallback = callback
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action) {
            val status = intent.extras?.get(SmsRetriever.EXTRA_STATUS) as Status
            when (status.statusCode) {
                CommonStatusCodes.SUCCESS -> {
                    val sms = intent.extras?.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
                    // 提取6位数字OTP
                    "\\d{6}".toRegex().find(sms)?.value?.let {
                        otpCallback?.invoke(it)
                    }
                }
                CommonStatusCodes.TIMEOUT -> {
                    // 超时未收到短信,提示手动输入
                }
            }
        }
    }
}

步骤3:在Compose中集成

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import com.google.android.gms.auth.api.phone.SmsRetriever

@Composable
fun OTPWithSMSRetriever() {
    val otpValue = rememberSaveable { mutableStateOf("") }
    val context = LocalContext.current
    val smsReceiver = remember { SMSReceiver() }

    LaunchedEffect(Unit) {
        val filter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
        context.registerReceiver(smsReceiver, filter)

        smsReceiver.setOtpCallback { otp ->
            otpValue.value = otp
        }

        // 启动短信监听
        SmsRetriever.getClient(context).startSmsRetriever().apply {
            addOnSuccessListener { /* 监听启动成功 */ }
            addOnFailureListener { /* 启动失败,降级手动输入 */ }
        }

        onDispose {
            context.unregisterReceiver(smsReceiver)
        }
    }

    OTPTextField() // 复用之前的输入框组件
}

注意:使用SMS Retriever API时,短信内容必须包含应用签名生成的哈希值(格式如@your-app-domain #123456),否则无法被捕获。

内容的提问来源于stack exchange,提问作者MaheshGubbi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:37:28