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

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

我想自动捕获或读取短信的 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 is your code to log in.

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

颤动自动填充:https://github.com/flutter/flutter/blob/7891006299/packages/flutter/lib/src/services/autofill.dart#L362

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

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

flutter dart autofill one-time-password
5个回答
16
投票

希望对于还在寻找这个问题答案的人来说,我还不算太晚。我使用了两个包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 之后,放置以下函数以获取传入的 SMS:

  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();
  }

你应该在你的处理函数中处理你不使用的任何东西:

  @override
  void dispose() {
    textEditingController1.dispose();
    AltSmsAutofill().unregisterListener();
    super.dispose();
  }

然后您需要将 pinfields 放在构建函数中或像这样的列中:

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 小部件,并在收到短信后使用字符串索引将代码设置为文本字段。例如,请参见下图。


4
投票

您可以使用这个包:https://pub.dev/packages/sms_autofill

但请考虑以下限制:

Android 短信约束 对于要接收的代码,需要遵循 此处描述的一些规则: https://developers.google.com/identity/sms-retriever/verify

不超过 140 字节 以前缀开头 <#> 包含一个 客户端发送回您的服务器以完成的一次性代码 验证流程以 11 个字符的哈希字符串结束 识别您的应用 SMS 的一个示例是:

<#> 示例应用程序:您的代码是 123456 FA+9qCX9VSu


1
投票

我用这个

package
来接收短信检查

它的作用是通过它的监听器监听 SMS,当 SMS 到达时打印 SMS。

这是

Code
,我前段时间为此写过(我不确定这个包是否做了一些更改或更新,因为我已经有一段时间没有使用它了,但那时候它工作得很好.),

SmsReceiver receiver = new SmsReceiver();
await receiver.onSmsReceived.listen((SmsMessage msg) => checkSMS(msg));

SMS
体的打印方法,

  checkSMS(SmsMessage msg) async {
    print(msg.body);
  }

现在您可以自动填充

SMS
并使用
OTP
中的一些
regex
取出
msg.body
并将其设置为
TextFieldController
文本以进行自动填充。

注意:它会获取每条短信,因此要获取唯一一条您需要的短信,您必须检查关键字或在您身边设置一些正则表达式以仅显示 OTP 消息或消息中的公司名称。


1
投票

@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 返回 iOS 版本(alt_sms_autofill: ^1.0.0)。因此,如果您在将 otp 分配给控制器之前使用某种验证,您将摆脱一切。


0
投票

我使用 https://pub.dev/packages/sms_autofill 包来读取 OTP - 代码是

Pinput(
                                          androidSmsAutofillMethod:
                                              AndroidSmsAutofillMethod
                                                  .smsRetrieverApi,
                                          listenForMultipleSmsOnAndroid:
                                              true,
                                          controller: otpController,
                                          focusNode: otpFocus,
                                          pinputAutovalidateMode:
                                              PinputAutovalidateMode
                                                  .onSubmit,
                                          inputFormatters: [
                                            FilteringTextInputFormatter
                                                .digitsOnly
                                          ],
                                          validator: (value) {
                                            if (value == "" ||
                                                value == null ||
                                                value.length < 4) {
                                              otpFocus.unfocus();
                                              return "Enter your 4 digit Otp";
                                            }
                                            if (otpError != '') {
                                              return 'Invalid Otp';
                                            }
                                            return null;
                                          },
                                          onChanged: (pin) {
                                            setState(() {
                                              otpError = '';
                                            });
                                          },
                                          onCompleted: (pin) async {
                                            otpFocus.unfocus();
                                            setState(() {
                                              otpError = '';
                                            });
                                            btnVerifyController.start();
                                          },
                                          focusedPinTheme:
                                              defaultPinTheme.copyWith(
                                            decoration: defaultPinTheme
                                                .decoration!
                                                .copyWith(
                                              borderRadius:
                                                  BorderRadius.circular(8),
                                              border: Border.all(
                                                  color:
                                                      focusedBorderColor),
                                            ),
                                          ),
                                          submittedPinTheme:
                                              defaultPinTheme.copyWith(
                                            decoration: defaultPinTheme
                                                .decoration!
                                                .copyWith(
                                              color: fillColor,
                                              borderRadius:
                                                  BorderRadius.circular(19),
                                              border: Border.all(
                                                  color:
                                                      focusedBorderColor),
                                            ),
                                          ),
                                          errorPinTheme: defaultPinTheme
                                              .copyBorderWith(
                                            border: Border.all(
                                                color: Colors.redAccent),
                                          ),
                                        ),

我在登录页面调用了这个函数 var appSignatureID = await SmsAutoFill().getAppSignature; 我添加了这段代码并在小部件中使用了这个 appSignatureID 变量来显示 UI 中的值 - 并在内部测试中添加了该版本,因此我可以在 UI 中获取应用程序签名 ID 并且我注意到了它。 之后,我删除了在 UI 中显示的代码,并将该密钥发送到短信模板的后端

© www.soinside.com 2019 - 2024. All rights reserved.