Flutter一次性密码服务插件otp_service的使用

Flutter一次性密码服务插件otp_service的使用

本插件旨在帮助开发者在Flutter应用中集成一次性密码(One-Time Password)功能。通过该插件,用户可以轻松实现短信验证码、邮件验证码等功能。

开始使用

首先,确保你已经添加了otp_service插件到你的pubspec.yaml文件中:

dependencies:
  otp_service: ^1.0.0

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

完整示例代码

以下是一个完整的示例代码,展示了如何在Flutter应用中使用otp_service插件。

示例代码

import 'dart:async';

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

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

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

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isLimited = false;
  int _countTime = 0;
  late Timer _timer;
  String _error = '';
  String _otp = '';
  final _focusNode = FocusNode();
  
  [@override](/user/override)
  void initState() {
    super.initState();
    _otp = '';
    _countTime = 60;
    _isLimited = false;
    _startTimer();
  }

  [@override](/user/override)
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  void _startTimer() {
    const oneSec = Duration(seconds: 1);
    _timer = Timer.periodic(
      oneSec,
      (Timer timer) {
        if (_countTime == 0) {
          setState(() {
            timer.cancel();
          });
        } else {
          setState(() {
            _countTime--;
          });
        }
      },
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: GestureDetector(
        onTap: () => FocusScope.of(context).unfocus(),
        child: Scaffold(
          appBar: AppBar(
            title: const Text('OTP 示例'),
          ),
          body: SingleChildScrollView(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  OTPWidgetService(
                    onKeyboard: (isShow) {
                      debugPrint('onKeyboard: $isShow');
                    },
                    errorString: _error,
                    errorTextStyle: TextStyle(color: Colors.red[300]),
                    lineColor: Colors.black26,
                    onTap: () {
                      debugPrint('onTap');
                    },
                    onChanged: (otp) {
                      _otp = otp;
                      debugPrint('OTP: $otp');
                      setState(() {
                        _error = '';
                      });
                    },
                    textStyle: TextStyle(color: Colors.black, fontSize: 36),
                  ),
                  const SizedBox(height: 16),
                  OutlinedButton(
                    onPressed: () {
                      setState(() {
                        _error = _otp.isEmpty ? '此字段是必需的。' : _otp.length < 6 ? '验证码无效。' : '';
                      });
                    },
                    child: Text("验证"),
                  ),
                  const SizedBox(height: 16),
                  if (_countTime > 0)
                    RichText(
                      text: TextSpan(
                        children: [
                          TextSpan(
                              text: _isLimited
                                  ? '重发限制已达到。您可以在24小时后重新发送验证码('
                                  : '您可以在',
                              style: TextStyle(color: Colors.black)),
                          TextSpan(
                              text: _isLimited ? formatOTPTime : formatOTPTimeNotHour,
                              style: TextStyle(color: Colors.black26)),
                          TextSpan(
                              text: _isLimited ? ')秒后重试。' : '秒后重试。',
                              style: TextStyle(color: Colors.black)),
                        ],
                      ),
                    )
                  else
                    OutlinedButton(
                      onPressed: () {
                        _countTime = 60;
                        _startTimer();
                      },
                      child: Text("重新发送验证码"),
                    ),
                  const SizedBox(height: 16),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  Widget buildNoteWidget() {
    return RichText(
      text: TextSpan(
        children: [
          TextSpan(
              text: _isLimited
                  ? '重发限制已达到。您可以在24小时后重新发送验证码('
                  : '您可以在',
              style: TextStyle(color: Colors.black),),
          TextSpan(
              text: _isLimited ? formatOTPTime : formatOTPTimeNotHour,
              style: TextStyle(color: Colors.black54)),
          TextSpan(
              text: _isLimited ? ')秒后重试。' : '秒后重试。',
              style: TextStyle(color: Colors.black)),
        ],
      ),
    );
  }

  String get formatOTPTime => _formatOTPTime();

  String get formatOTPTimeNotHour => _formatOTPTime(isFull: false);

  String _formatOTPTime({bool isFull = true}) {
    final duration = Duration(seconds: _countTime);
    String twoDigits(int n) => n.toString().padLeft(2, '0');
    final hours = twoDigits(duration.inHours);
    final minutes = twoDigits(duration.inMinutes.remainder(60));
    final seconds = twoDigits(duration.inSeconds.remainder(60));
    return [if (isFull) hours, minutes, seconds].join(':');
  }
}

代码解释

  1. 导入必要的库

    import 'dart:async';
    import 'package:flutter/material.dart';
    import 'package:otp_service/otp_service.dart';
    
  2. 主函数

    void main() {
      runApp(const MyApp());
    }
    
  3. 定义主应用状态

    class MyApp extends StatefulWidget {
      const MyApp({super.key});
    
      [@override](/user/override)
      State<MyApp> createState() => _MyAppState();
    }
    
  4. 定义状态类

    class _MyAppState extends State<MyApp> {
      bool _isLimited = false;
      int _countTime = 0;
      late Timer _timer;
      String _error = '';
      String _otp = '';
      final _focusNode = FocusNode();
      
      [@override](/user/override)
      void initState() {
        super.initState();
        _otp = '';
        _countTime = 60;
        _isLimited = false;
        _startTimer();
      }
    
      [@override](/user/override)
      void dispose() {
        _timer.cancel();
        super.dispose();
      }
    
  5. 启动计时器

    void _startTimer() {
      const oneSec = Duration(seconds: 1);
      _timer = Timer.periodic(
        oneSec,
        (Timer timer) {
          if (_countTime == 0) {
            setState(() {
              timer.cancel();
            });
          } else {
            setState(() {
              _countTime--;
            });
          }
        },
      );
    }
    
  6. 构建应用UI

    [@override](/user/override)
    Widget build(BuildContext context) {
      return MaterialApp(
        home: GestureDetector(
          onTap: () => FocusScope.of(context).unfocus(),
          child: Scaffold(
            appBar: AppBar(
              title: const Text('OTP 示例'),
            ),
            body: SingleChildScrollView(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    OTPWidgetService(
                      onKeyboard: (isShow) {
                        debugPrint('onKeyboard: $isShow');
                      },
                      errorString: _error,
                      errorTextStyle: TextStyle(color: Colors.red[300]),
                      lineColor: Colors.black26,
                      onTap: () {
                        debugPrint('onTap');
                      },
                      onChanged: (otp) {
                        _otp = otp;
                        debugPrint('OTP: $otp');
                        setState(() {
                          _error = '';
                        });
                      },
                      textStyle: TextStyle(color: Colors.black, fontSize: 36),
                    ),
                    const SizedBox(height: 16),
                    OutlinedButton(
                      onPressed: () {
                        setState(() {
                          _error = _otp.isEmpty ? '此字段是必需的。' : _otp.length < 6 ? '验证码无效。' : '';
                        });
                      },
                      child: Text("验证"),
                    ),
                    const SizedBox(height: 16),
                    if (_countTime > 0)
                      RichText(
                        text: TextSpan(
                          children: [
                            TextSpan(
                                text: _isLimited
                                    ? '重发限制已达到。您可以在24小时后重新发送验证码('
                                    : '您可以在',
                                style: TextStyle(color: Colors.black)),
                            TextSpan(
                                text: _isLimited ? formatOTPTime : formatOTPTimeNotHour,
                                style: TextStyle(color: Colors.black26)),
                            TextSpan(
                                text: _isLimited ? ')秒后重试。' : '秒后重试。',
                                style: TextStyle(color: Colors.black)),
                          ],
                        ),
                      )
                    else
                      OutlinedButton(
                        onPressed: () {
                          _countTime = 60;
                          _startTimer();
                        },
                        child: Text("重新发送验证码"),
                      ),
                    const SizedBox(height: 16),
                  ],
                ),
              ),
            ),
          ),
        ),
      );
    }
    
  7. 其他辅助方法

    Widget buildNoteWidget() {
      return RichText(
        text: TextSpan(
          children: [
            TextSpan(
                text: _isLimited
                    ? '重发限制已达到。您可以在24小时后重新发送验证码('
                    : '您可以在',
                style: TextStyle(color: Colors.black),),
            TextSpan(
                text: _isLimited ? formatOTPTime : formatOTPTimeNotHour,
                style: TextStyle(color: Colors.black54)),
            TextSpan(
                text: _isLimited ? ')秒后重试。' : '秒后重试。',
                style: TextStyle(color: Colors.black)),
          ],
        ),
      );
    }
    
    String get formatOTPTime => _formatOTPTime();
    
    String get formatOTPTimeNotHour => _formatOTPTime(isFull: false);
    
    String _formatOTPTime({bool isFull = true}) {
      final duration = Duration(seconds: _countTime);
      String twoDigits(int n) => n.toString().padLeft(2, '0');
      final hours = twoDigits(duration.inHours);
      final minutes = twoDigits(duration.inMinutes.remainder(60));
      final seconds = twoDigits(duration.inSeconds.remainder(60));
      return [if (isFull) hours, minutes, seconds].join(':');
    }
    

更多关于Flutter一次性密码服务插件otp_service的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


otp_service 是一个用于在 Flutter 应用中生成和处理一次性密码(OTP)的插件。它可以帮助你实现基于时间的 OTP(TOTP)或基于计数器的 OTP(HOTP),广泛应用于双因素认证(2FA)场景。

以下是如何在 Flutter 项目中使用 otp_service 插件的步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 otp_service 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  otp_service: ^1.0.0  # 请使用最新版本

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

2. 导入插件

在需要使用 OTP 服务的 Dart 文件中导入 otp_service 插件:

import 'package:otp_service/otp_service.dart';

3. 生成 OTP

使用 OtpService 类来生成 OTP。你可以生成基于时间的 OTP(TOTP)或基于计数器的 OTP(HOTP)。

生成 TOTP

String secret = 'JBSWY3DPEHPK3PXP';  // 你的密钥
int period = 30;  // 有效期(秒)
int digits = 6;  // OTP 位数

String totp = OtpService.generateTOTP(secret, period, digits);
print('Generated TOTP: $totp');

生成 HOTP

String secret = 'JBSWY3DPEHPK3PXP';  // 你的密钥
int counter = 1;  // 计数器值
int digits = 6;  // OTP 位数

String hotp = OtpService.generateHOTP(secret, counter, digits);
print('Generated HOTP: $hotp');

4. 验证 OTP

你可以使用 OtpService 来验证用户输入的 OTP 是否正确。

验证 TOTP

String userInput = '123456';  // 用户输入的 OTP
String secret = 'JBSWY3DPEHPK3PXP';  // 你的密钥
int period = 30;  // 有效期(秒)
int digits = 6;  // OTP 位数

bool isValid = OtpService.validateTOTP(userInput, secret, period, digits);
print('Is TOTP valid? $isValid');

验证 HOTP

String userInput = '123456';  // 用户输入的 OTP
String secret = 'JBSWY3DPEHPK3PXP';  // 你的密钥
int counter = 1;  // 计数器值
int digits = 6;  // OTP 位数

bool isValid = OtpService.validateHOTP(userInput, secret, counter, digits);
print('Is HOTP valid? $isValid');

5. 处理 OTP URI

otp_service 还支持解析和生成 OTP URI,通常用于二维码生成和扫描。

解析 OTP URI

String otpUri = 'otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&algorithm=SHA1&digits=6&period=30';

OtpUri parsedUri = OtpService.parseOtpUri(otpUri);
print('Secret: ${parsedUri.secret}');
print('Issuer: ${parsedUri.issuer}');
print('Algorithm: ${parsedUri.algorithm}');
print('Digits: ${parsedUri.digits}');
print('Period: ${parsedUri.period}');

生成 OTP URI

String otpUri = OtpService.generateOtpUri(
  type: OtpType.totp,
  secret: 'JBSWY3DPEHPK3PXP',
  issuer: 'Example',
  accountName: 'alice@google.com',
  algorithm: 'SHA1',
  digits: 6,
  period: 30,
);

print('Generated OTP URI: $otpUri');

6. 处理异常

在使用 otp_service 时,可能会遇到一些异常情况,例如无效的密钥或 URI。你可以使用 try-catch 块来处理这些异常:

try {
  String totp = OtpService.generateTOTP(secret, period, digits);
  print('Generated TOTP: $totp');
} catch (e) {
  print('Error generating TOTP: $e');
}

7. 完整示例

以下是一个完整的示例,展示了如何使用 otp_service 生成和验证 TOTP:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('OTP Service Example'),
        ),
        body: Center(
          child: OTPExample(),
        ),
      ),
    );
  }
}

class OTPExample extends StatelessWidget {
  final String secret = 'JBSWY3DPEHPK3PXP';
  final int period = 30;
  final int digits = 6;

  [@override](/user/override)
  Widget build(BuildContext context) {
    String totp = OtpService.generateTOTP(secret, period, digits);
    bool isValid = OtpService.validateTOTP(totp, secret, period, digits);

    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('Generated TOTP: $totp'),
        SizedBox(height: 20),
        Text('Is TOTP valid? $isValid'),
      ],
    );
  }
}
回到顶部