如何从短信中获取OTP并自动填充?

17

我希望能够自动捕获或读取短信消息的OTP。我进行了一些测试,例如这段代码:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Demo Auto OTP'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController _textController = TextEditingController();
  String _error;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Multi-Factor-Authentication"),
        ),
        body: Form(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: _textController,
                autofillHints: [ AutofillHints.oneTimeCode ],
                keyboardType: TextInputType.visiblePassword,
                maxLength: 6,
                maxLengthEnforced: true,
                style: TextStyle(fontSize: 32),
              ),

              RaisedButton(
                child: Text("Verify"),
                onPressed: () => Navigator.of(context).pop(_textController.value.text),
              ),
            ],
          ),
        )
    );
  }
}

这是测试短信:12345是您的登录代码。

Flutter oneTimeCode 的文档: https://api.flutter.dev/flutter/services/AutofillHints/oneTimeCode-constant.html

Flutter Autofill: https://github.com/flutter/flutter/blob/7891006299/packages/flutter/lib/src/services/autofill.dart#L362

iOS: https://developer.apple.com/documentation/uikit/uitextcontenttype

Android: https://developer.android.com/reference/androidx/autofill/HintConstants#AUTOFILL_HINT_SMS_OTP

5个回答

23

新回答(截至2023年6月): 我们将使用此软件包来自动填充短信验证码:https://pub.dev/packages/sms_autofill。将其添加到您的pubspec.yaml文件中。 首先,确保您在Android手机上收到的短信开头应包含“<#>”,并且在短信结尾处包含您的应用程序签名密钥。示例短信的格式如下:

<#> 您的验证码是543216 app_signature

有多种方法可以生成应用程序签名密钥,您还可以使用sms_autofill软件包,使用以下代码获取您的应用程序签名密钥:

await SmsAutoFill().getAppSignature;

它将以字符串格式返回您的应用程序签名,只需将其添加到短信的末尾。如果您已经有了应用程序签名,则可以删除上面的代码或忽略它。

首先让我们绘制小部件:

PinFieldAutoFill(
                        decoration: BoxLooseDecoration(
                          strokeColorBuilder: PinListenColorBuilder(Colors.black, Colors.black26),
                          bgColorBuilder: const FixedColorBuilder(Colors.white),
                          strokeWidth: 2,
                        ),
                        autoFocus: true,
                        cursor: Cursor(color: Colors.red, enabled: true, width: 1),
                        currentCode: '',
                        onCodeSubmitted: (code) {},
                        codeLength: 6,
                        onCodeChanged: (code) {
                          print(code);
                        },
                      ),

然后在您的onInit()状态中监听短信代码:

await SmsAutoFill().listenForCode();

这将在5分钟内监听带有验证码的短信,一旦接收到,将自动填充上述小部件。
在您的onDispose方法中,请不要忘记释放监听器:
SmsAutoFill().unregisterListener();

旧答案: 如果有人想阅读用户的短信,可以使用此答案。此外,如果您不提供适当的解释,PlayStore可能会拒绝您的应用程序,原因是您想要阅读用户的短信。对于仍在寻找此问题答案的人们,我希望我没有来得太晚。我使用了两个软件包https://pub.dev/packages/alt_sms_autofillhttps://pub.dev/packages/pin_code_fields。将这两个软件包添加到您的pubspec.yaml文件中。运行'flutter pub get'以下载这些软件包。

在您的otp屏幕上导入这两个软件包:

import 'package:alt_sms_autofill/alt_sms_autofill.dart';
import 'package:pin_code_fields/pin_code_fields.dart';

在您的AppState扩展State后,将以下函数放入以获取传入的短信:
  TextEditingController textEditingController1;

  String _comingSms = 'Unknown';

  Future<void> initSmsListener() async {

    String comingSms;
    try {
      comingSms = await AltSmsAutofill().listenForSms;
    } on PlatformException {
      comingSms = 'Failed to get Sms.';
    }
    if (!mounted) return;
    setState(() {
      _comingSms = comingSms;
      print("====>Message: ${_comingSms}");
      print("${_comingSms[32]}");
      textEditingController1.text = _comingSms[32] + _comingSms[33] + _comingSms[34] + _comingSms[35]
          + _comingSms[36] + _comingSms[37]; //used to set the code in the message to a string and setting it to a textcontroller. message length is 38. so my code is in string index 32-37.
    });
  }

我的传入OTP消息格式如下: 您的手机验证代码是625742。 在上述函数中,它会监听传入的短信并将其保存到一个字符串中。在短信接收后,我通过给出字符串中代码的索引位置,将'625742'代码设置为我的文本编辑控制器,然后将值设置为稍后您将看到的PinFields。
在您的initState中调用函数:
  @override
  void initState() {
    super.initState();
    textEditingController1 = TextEditingController();
    initSmsListener();
  }

你应该在你的dispose函数中处理掉任何你不再使用的东西。
  @override
  void dispose() {
    textEditingController1.dispose();
    AltSmsAutofill().unregisterListener();
    super.dispose();
  }

然后你需要将pinfields放在你的build函数中或者放在一个column里面,就像这样:
PinCodeTextField(
          appContext: context,
          pastedTextStyle: TextStyle(
            color: Colors.green.shade600,
            fontWeight: FontWeight.bold,
          ),
          length: 6,
          obscureText: false,
          animationType: AnimationType.fade,
          pinTheme: PinTheme(
            shape: PinCodeFieldShape.box,
            borderRadius: BorderRadius.circular(10),
            fieldHeight: 50,
            fieldWidth: 40,
            inactiveFillColor: Colors.white,
            inactiveColor: ColorUtils.greyBorderColor,
            selectedColor: ColorUtils.greyBorderColor,
            selectedFillColor: Colors.white,
            activeFillColor: Colors.white,
            activeColor: ColorUtils.greyBorderColor
          ),
          cursorColor: Colors.black,
          animationDuration: Duration(milliseconds: 300),
          enableActiveFill: true,
          controller: textEditingController1,
          keyboardType: TextInputType.number,
          boxShadows: [
            BoxShadow(
              offset: Offset(0, 1),
              color: Colors.black12,
              blurRadius: 10,
            )
          ],
          onCompleted: (v) {
            //do something or move to next screen when code complete
          },
          onChanged: (value) {
            print(value);
            setState(() {
              print('$value');
            });
          },
        ),

确保将控制器设置为pinfield小部件,并在收到短信后使用字符串索引将代码设置到您的文本字段中。请参考下面的图片示例。 enter image description here

1
提醒一下,如果您使用此软件包,请确保在上传应用程序到PlayStore之前阅读有关阅读用户短信的所有条款和条件。我的应用程序因为我无法给他们一个合适的理由来解释我为什么使用该软件包而被多次拒绝。 - Md. Kamrul Amin
这绝对是目前最好的用于读取短信代码的软件包! - Shrijan Regmi
它应该适用于iOS和Android @AhmedNabil。如果您尝试了,请评论以确认。 - Md. Kamrul Amin
@Md.KamrulAmin,你有没有想过我们该怎么解决这个问题?谷歌应用商店也拒绝了我的应用。这意味着我不能在实际应用中使用它吗?如果不行,请给出另一种方案。 - Ashu Pathak
@Md.KamrulAmin 我该怎么处理这个问题?Play Store也拒绝了我的应用。 - Nehal Jaisalmeria
显示剩余6条评论

5
你可以使用这个包:https://pub.dev/packages/sms_autofill,但需要考虑以下限制:
Android 短信约束:为接收代码,必须遵循一些规则,如此处所述: https://developers.google.com/identity/sms-retriever/verify。 短信长度不能超过140字节,必须以前缀 <#> 开始,包含客户端发送回您的服务器以完成验证流程的一次性代码,并以11个字符的哈希字符串结尾来标识您的应用程序。 一个SMS示例可能是: <#> ExampleApp: Your code is 123456 FA+9qCX9VSu

是的,iOS受支持,并且比Android更容易,检查软件包。 - AVEbrahimi

3

@Kamrul Hasan Jony的描述非常好。但是在实施此过程时应该注意一件事情。 在将OTP分配给控制器之前,您应该检查app_Signature或其他验证,以便OTP仅从特定消息中获取。例如:

setState(() {
  _commingSms = commingSms;
  String aStr = _commingSms.replaceAll(new RegExp(r'[^0-9]'),'');
  String otp = aStr.substring(0,4);
  if(_commingSms.contains(AppConstants.appSignature)){
    textEditingController.text = otp;
    //_presenter.validateOtp(widget.apiToken, widget.phone, textEditingController.text, context);
  }
});

此外,对于iOS版本,AltSmsAutofill().listenForSms返回(alt_sms_autofill: ^1.0.0)。因此,在将otp分配给控制器之前使用某种验证方法可以避免出现任何问题。请注意保留HTML标记。

2
我曾使用这个来接收短信查看它
它的作用是通过监听器侦听短信,当短信到达时打印出短信内容。
以下是我写的代码(我不确定该包是否进行了一些更改或更新,因为我有一段时间没有使用它,但那时它完美地工作)。
SmsReceiver receiver = new SmsReceiver();
await receiver.onSmsReceived.listen((SmsMessage msg) => checkSMS(msg));

打印SMS正文的方法:
  checkSMS(SmsMessage msg) async {
    print(msg.body);
  }

现在,您可以使用一些正则表达式从msg.body中自动填充SMS并获取OTP,并将其设置为TextFieldController文本以进行自动填充。
注意:它将获取每个SMS,因此要获取您需要的内容,您必须检查关键字或在您的一侧设置一些正则表达式以仅显示OTP消息或消息中的公司名称。

@khaled09909,它应该自动适用于iOS,因为这是iOS内置功能。只需对输入字段进行自动对焦,它就可以自动填充。如果我错了,请纠正我。 - Mukul
请访问此链接以获取更多细节:@khaled09909 https://stackoverflow.com/questions/65562773/how-can-i-listen-for-code-in-sms-on-ios-with-flutter - Mukul

1
在Flutter中,我使用了https://pub.dev/packages/sms_autofill包来读取OTP - 代码如下:
Pinput(
   androidSmsAutofillMethod:AndroidSmsAutofillMethod.smsRetrieverApi,
   listenForMultipleSmsOnAndroid:true,
   controller: otpController,
   focusNode: otpFocus,
   pinputAutovalidateMode:PinputAutovalidateMode.onSubmit,
   inputFormatters: [
    FilteringTextInputFormatter.digitsOnly
   ],
)

我在登录页面调用了这个函数: var appSignatureID = await SmsAutoFill().getAppSignature; 我添加了这段代码并将其中的 appSignatureID 变量用于 Text widget 中,在 UI 中显示值,并添加了发布的 AAB Bundle 进行内部测试,这样我就可以在 UI 中获取应用程序签名 ID 并记录下来。 之后,我将代码从 UI 中移除,并将记录的键发送到后端用于短信模板。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接