Flutter一次性密码自动填充插件otp_autofill_plus的使用

发布于 1周前 作者 yibo5220 来自 Flutter

Flutter一次性密码自动填充插件otp_autofill_plus的使用

otp_autofill_plus 是一个用于在Flutter应用中实现一次性密码(OTP)自动填充的插件。它利用了Android的SMS User Consent API和SMS Retriever API以及iOS内置的文本字段自动填充功能。

描述

该插件允许你通过监听短信来自动填充一次性密码,支持自定义策略以从其他输入源获取验证码(例如推送通知)。

iOS

在iOS上,OTP自动填充是内置于TextField中的。短信内容必须包含单词“code”或其翻译,并且短信中只能有一个数字序列。

iOS测试

iOS可以从任何其他号码接收验证码。

Android

在Android上,插件提供了多种方法来启动监听验证码的功能:

  • OTPInteractor.hint:显示系统对话框以选择保存的电话号码。
  • OTPInteractor.getAppSignature:创建应用程序的哈希码,用于SMS Retriever API。
  • OTPInteractor.startListenUserConsentOTPInteractor.startListenRetriever:开始监听来自Google服务的验证码,持续5分钟。
  • OTPInteractor.stopListenForCode:在dispose时使用。

安装

pubspec.yaml文件中添加依赖:

dependencies:
  otp_autofill_plus: ^当前版本号

确保Android项目的minSdkVersion至少为21:

android {
  ...
  defaultConfig {
    ...
    minSdkVersion 21
    ...
  }
  ...
}

使用示例

创建简单策略

class SampleStrategy extends OTPStrategy {
  @override
  Future<String> listenForCode() {
    return Future.delayed(
      const Duration(seconds: 4),
      () => 'Your code is 54321',
    );
  }
}

初始化监听器并设置

import 'package:flutter/material.dart';
import 'package:otp_autofill_plus/otp_autofill_plus.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late OTPTextEditController controller;
  late OTPInteractor _otpInteractor;

  @override
  void initState() {
    super.initState();
    _initInteractor();
    controller = OTPTextEditController(
      codeLength: 5,
      onCodeReceive: (code) => print('Your Application receive code - $code'),
      otpInteractor: _otpInteractor,
    )..startListenUserConsent(
        (code) {
          final exp = RegExp(r'(\d{5})');
          return exp.stringMatch(code ?? '') ?? '';
        },
        strategies: [
          SampleStrategy(),
        ],
      );
  }

  Future<void> _initInteractor() async {
    _otpInteractor = OTPInteractor();

    final appSignature = await _otpInteractor.getAppSignature();
    print('Your app signature: $appSignature');
  }

  @override
  void dispose() {
    controller.stopListen();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Plugin example app'),
        ),
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(40),
            child: TextField(
              textAlign: TextAlign.center,
              keyboardType: TextInputType.number,
              controller: controller,
            ),
          ),
        ),
      ),
    );
  }
}

发送新代码

当超时异常发生时,可以重新启动监听以获取新的验证码:

controller = OTPTextEditController(
  codeLength: 5,
  onCodeReceive: (code) => print('Your Application receive code - $code'),
  otpInteractor: _otpInteractor,
  onTimeOutException: () {
    controller.startListenUserConsent(
      (code) {
        final exp = RegExp(r'(\d{5})');
        return exp.stringMatch(code ?? '') ?? '';
      },
      strategies: [
        SampleStrategy(),
      ],
    );
  },
)..startListenUserConsent(
  (code) {
    final exp = RegExp(r'(\d{5})');
    return exp.stringMatch(code ?? '') ?? '';
  },
  strategies: [
    SampleStrategy(),
  ],
);

以上是一个完整的示例demo,展示了如何在Flutter应用中使用otp_autofill_plus插件来实现一次性密码的自动填充功能。


更多关于Flutter一次性密码自动填充插件otp_autofill_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter一次性密码自动填充插件otp_autofill_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于otp_autofill_plus插件在Flutter中的使用,以下是一个简单的代码示例,展示了如何集成和使用这个插件来实现一次性密码(OTP)的自动填充功能。

首先,确保你已经在pubspec.yaml文件中添加了otp_autofill_plus依赖:

dependencies:
  flutter:
    sdk: flutter
  otp_autofill_plus: ^x.y.z  # 请替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,是一个完整的Flutter应用示例,展示了如何使用otp_autofill_plus

import 'package:flutter/material.dart';
import 'package:otp_autofill_plus/otp_autofill_plus.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'OTP Autofill Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: OtpScreen(),
    );
  }
}

class OtpScreen extends StatefulWidget {
  @override
  _OtpScreenState createState() => _OtpScreenState();
}

class _OtpScreenState extends State<OtpScreen> {
  final TextEditingController _controller1 = TextEditingController();
  final TextEditingController _controller2 = TextEditingController();
  final TextEditingController _controller3 = TextEditingController();
  final TextEditingController _controller4 = TextEditingController();
  final TextEditingController _controller5 = TextEditingController();
  final TextEditingController _controller6 = TextEditingController();

  @override
  void dispose() {
    _controller1.dispose();
    _controller2.dispose();
    _controller3.dispose();
    _controller4.dispose();
    _controller5.dispose();
    _controller6.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('OTP Autofill Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            TextField(
              controller: _controller1,
              keyboardType: TextInputType.number,
              maxLength: 1,
              decoration: InputDecoration(
                labelText: '1',
                border: OutlineInputBorder(),
              ),
              inputFormatters: [
                FilteringTextInputFormatter.digitsOnly,
              ],
              onSubmitted: (value) {
                FocusScope.of(context).requestFocus(_controller2.focusNode);
              },
            ),
            TextField(
              controller: _controller2,
              keyboardType: TextInputType.number,
              maxLength: 1,
              decoration: InputDecoration(
                labelText: '2',
                border: OutlineInputBorder(),
              ),
              inputFormatters: [
                FilteringTextInputFormatter.digitsOnly,
              ],
              onSubmitted: (value) {
                FocusScope.of(context).requestFocus(_controller3.focusNode);
              },
            ),
            TextField(
              controller: _controller3,
              keyboardType: TextInputType.number,
              maxLength: 1,
              decoration: InputDecoration(
                labelText: '3',
                border: OutlineInputBorder(),
              ),
              inputFormatters: [
                FilteringTextInputFormatter.digitsOnly,
              ],
              onSubmitted: (value) {
                FocusScope.of(context).requestFocus(_controller4.focusNode);
              },
            ),
            TextField(
              controller: _controller4,
              keyboardType: TextInputType.number,
              maxLength: 1,
              decoration: InputDecoration(
                labelText: '4',
                border: OutlineInputBorder(),
              ),
              inputFormatters: [
                FilteringTextInputFormatter.digitsOnly,
              ],
              onSubmitted: (value) {
                FocusScope.of(context).requestFocus(_controller5.focusNode);
              },
            ),
            TextField(
              controller: _controller5,
              keyboardType: TextInputType.number,
              maxLength: 1,
              decoration: InputDecoration(
                labelText: '5',
                border: OutlineInputBorder(),
              ),
              inputFormatters: [
                FilteringTextInputFormatter.digitsOnly,
              ],
              onSubmitted: (value) {
                FocusScope.of(context).requestFocus(_controller6.focusNode);
              },
            ),
            TextField(
              controller: _controller6,
              keyboardType: TextInputType.number,
              maxLength: 1,
              decoration: InputDecoration(
                labelText: '6',
                border: OutlineInputBorder(),
              ),
              inputFormatters: [
                FilteringTextInputFormatter.digitsOnly,
              ],
              onEditingComplete: () {
                // OTP entered completely, perform action
                String otp = _controller1.text +
                    _controller2.text +
                    _controller3.text +
                    _controller4.text +
                    _controller5.text +
                    _controller6.text;
                print('OTP: $otp');
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                // Simulate fetching OTP
                // Here you would typically request an OTP from your server
                // and display it to the user via SMS/Email/etc.
                // For demo purposes, we'll just simulate filling the OTP
                await OtpAutofill.fillOtp(
                  context,
                  code: '123456',
                  message: 'Your OTP is 123456',
                );
              },
              child: Text('Simulate OTP'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了六个TextField来分别接收OTP的每一位数字。每个TextField都设置了maxLength为1,并且只接受数字输入。当用户输入完一个数字后,焦点会自动跳转到下一个TextField。当最后一个TextField完成编辑时,会拼接所有的数字并打印出来。

此外,我们还添加了一个按钮来模拟OTP的填充。在实际应用中,这个按钮可能会触发向用户发送OTP的操作(如通过SMS)。在这个示例中,我们直接调用OtpAutofill.fillOtp方法来模拟填充OTP。

请注意,为了使OtpAutofill.fillOtp方法正常工作,你的应用需要满足一些条件,比如正确的Android和iOS配置,以及正确的SMS检索权限等。这些配置通常涉及到操作系统的特定设置和权限请求,具体细节可以参考otp_autofill_plus的官方文档。

回到顶部