Flutter一次性密码(OTP)处理插件widget_toolkit_otp的使用

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

Flutter一次性密码(OTP)处理插件widget_toolkit_otp的使用

介绍

widget_toolkit_otp 包含了一系列有用的组件,可以帮助你构建短信/验证码屏幕或工作流。该包中的组件包括输入PIN码、粘贴验证码、重新发送逻辑等。

SMS OTP Field

主要组件

SmsCodeField

SmsCodeField 是基于 Pinput 的封装,并提供了所有自定义选项。它支持自动填充和粘贴功能,默认情况下使用内部通信机制 (SmsCodeBloc)。如果你想独立使用此组件,可以将 useInternalCommunication 设置为 false

CountdownWidget

用于跟踪剩余时间。你可以提供 onCountdownTick 回调来跟踪任何变化。通过控制器可以重置倒计时并从自定义时间开始。

AutomatedResendCodeButton

管理自身的状态,按钮被按下后会经历多个状态(如加载中、已发送、禁用、可用、错误)。为了防止用户频繁点击,按钮在执行 onPressed 后会被禁用一段时间。

ResendCodeButton

可以由业务逻辑维护,默认使用 SmsCodeBloc 构建一个高度可定制化的 TextButton

SmsPhoneNumberField

提供电话号码和更新保存电话号码的回调,方便构建带有功能的字段。

ResendButtonTimer

包装了 CountdownWidget 并结合 SmsCodeBloc 提供的业务逻辑。

ValidityWidget

显示最新发送给用户的验证码的有效期,不能脱离 SmsCodeBloc 使用。

SmsCodeProvider

以用户友好的方式提供所有依赖项,并根据 bloc 的状态和事件安排页面内容。

安装设置

  1. 添加依赖:

    $ flutter pub add widget_toolkit widget_toolkit_otp
    
  2. 导入包:

    import 'package:widget_toolkit/widget_toolkit.dart';
    import 'package:widget_toolkit_otp/widget_toolkit_otp.dart';
    
  3. 设置 ThemeData

    return MaterialApp(
      theme: ThemeData.light().copyWith(
        colorScheme: ColorScheme.fromSwatch(),
        extensions: [
          WidgetToolkitTheme.light(),
          SmsCodeTheme.light(),
        ],
      ),
      darkTheme: ThemeData.dark().copyWith(
        colorScheme: ColorScheme.fromSwatch(),
        extensions: [
          WidgetToolkitTheme.dark(),
          SmsCodeTheme.dark(),
        ],
      ),
      ...
    );
    

使用方法

有两种使用方式:一是使用预构建的组件来可视化自己的业务逻辑;二是使用提供的 SmsCodeBloc 和安排页面上的组件。

示例代码

下面是一个完整的示例,展示了如何集成 SmsCodeProvider 功能:

import 'package:flutter/material.dart';
import 'package:widget_toolkit/text_field_dialog.dart';
import 'package:widget_toolkit/theme_data.dart';
import 'package:widget_toolkit_otp/widget_toolkit_otp.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light()
          .copyWith(colorScheme: ColorScheme.fromSwatch(), extensions: [
        WidgetToolkitTheme.light(),
        SmsCodeTheme.light(),
        TextFieldDialogTheme.light(),
      ]),
      darkTheme: ThemeData.dark()
          .copyWith(colorScheme: ColorScheme.fromSwatch(), extensions: [
        WidgetToolkitTheme.dark(),
        SmsCodeTheme.dark(),
        TextFieldDialogTheme.dark(),
      ]),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) => Scaffold(
        body: SafeArea(
          child: SmsCodeProvider(
            sentNewCodeActivationTime: 2,
            smsCodeService: FakeSmsCodeService(),
            builder: (state) => Padding(
              padding: const EdgeInsets.all(20),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  SmsPhoneNumberField(
                    builder: (context, number, onChanged) => TextFieldDialog(
                      label: 'Phone Number',
                      value: number,
                      validator: FakeTextFieldValidator(),
                      translateError: (Object error) => null,
                      onChanged: onChanged,
                    ),
                  ),
                  const Column(
                    children: [
                      Text(
                        '*hint* The correct code might be 0000...',
                        style: TextStyle(color: Colors.grey),
                      ),
                      SizedBox(height: 8),
                      SmsCodeField(),
                      SizedBox(height: 8),
                      ValidityWidget(),
                    ],
                  ),
                  SizedBox(
                    height: 85,
                    child: Column(
                      children: [
                        ResendCodeButton(
                            strings: CustomLocalizedStrings(context)),
                        const ResendButtonTimer(),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      );
}

class FakeTextFieldValidator extends TextFieldValidator<String> {
  @override
  Future<String> validateOnSubmit(String text) async => text;

  @override
  void validateOnType(String text) => true;
}

/// 自定义字符串用于 ResendCodeButton
class CustomLocalizedStrings extends LocalizedStrings {
  CustomLocalizedStrings(super.context);

  @override
  String get sendNewCode => 'Resend';

  @override
  String get codeSent => 'Code resent';
}

/// 实现 SMS 验证码逻辑的服务
class FakeSmsCodeService implements SmsCodeService {
  /// 确认输入的验证码是否正确
  @override
  Future<dynamic> confirmPhoneCode(String code) async {
    return code == '0000' ? true : throw Exception();
  }

  /// 获取用户的电话号码(带国家区号)
  @override
  Future<String> getFullPhoneNumber() async => '+38164 1234567';

  /// 更新用户的电话号码并返回完整号码
  @override
  Future<String> updatePhoneNumber(String newNumber) async => '+38164 1234567';

  /// 发送新的验证码到用户
  @override
  Future<bool> sendConfirmationSms(String usersPhoneNumber) async {
    await Future.delayed(const Duration(seconds: 3));
    return true;
  }

  /// 验证码有效期(秒)
  @override
  Future<int> getValidityTime(bool reset) async => 30;

  /// 重新发送验证码按钮禁用时间(秒)
  @override
  Future<int> getResendButtonThrottleTime(bool reset) async => 15;

  /// 验证码长度
  @override
  Future<int> getCodeLength() async => 4;
}

这个例子展示了如何创建一个包含电话号码输入框、验证码输入框、验证码有效时间和重新发送按钮的完整页面。


更多关于Flutter一次性密码(OTP)处理插件widget_toolkit_otp的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter一次性密码(OTP)处理插件widget_toolkit_otp的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter应用中使用widget_toolkit_otp插件来处理一次性密码(OTP)的一个简单示例。widget_toolkit_otp插件通常用于显示和验证OTP输入。

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

dependencies:
  flutter:
    sdk: flutter
  widget_toolkit_otp: ^最新版本号  # 请替换为实际的最新版本号

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

接下来,在你的Flutter应用中,你可以使用OTPTextField来创建一个OTP输入框。以下是一个完整的示例代码:

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

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

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

class OTPScreen extends StatefulWidget {
  @override
  _OTPScreenState createState() => _OTPScreenState();
}

class _OTPScreenState extends State<OTPScreen> {
  final _controller = TextEditingController();
  final _focusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('OTP Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Enter OTP',
              style: TextStyle(fontSize: 24),
            ),
            SizedBox(height: 20),
            OTPTextField(
              length: 6,  // OTP长度
              width: 50,  // 每个输入框的宽度
              controller: _controller,
              focusNode: _focusNode,
              onCompleted: (otp) {
                // 当OTP输入完成时调用
                print('OTP Entered: $otp');
                // 在这里可以添加验证OTP的逻辑
              },
              style: TextStyle(fontSize: 20),
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                filled: true,
                fillColor: Colors.grey[200]!,
                counterText: '',
              ),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                // 清除OTP输入框
                _controller.clear();
                _focusNode.unfocus();
              },
              child: Text('Clear'),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    _focusNode.dispose();
    super.dispose();
  }
}

在这个示例中,我们做了以下几件事:

  1. 导入依赖:导入了flutter/material.dartwidget_toolkit_otp/widget_toolkit_otp.dart
  2. 创建主应用:在MyApp中设置了应用的主题和主页。
  3. 创建OTP屏幕:在OTPScreen中创建了一个包含OTP输入框的页面。
  4. OTP输入框:使用OTPTextField创建了一个OTP输入框,并设置了长度、宽度、控制器、焦点节点和完成时的回调。
  5. 清除按钮:添加了一个按钮来清除OTP输入框的内容。

确保你已经正确安装了widget_toolkit_otp插件,并根据需要调整OTP长度、样式等参数。此示例仅用于展示如何使用该插件,实际应用中你可能需要根据具体需求添加更多的功能和验证逻辑。

回到顶部