写点什么

Flutter 完美的验证码输入框(2 种方法)【Flutter 专题 25】

作者:坚果前端
  • 2021 年 12 月 24 日
  • 本文字数:4611 字

    阅读完需:约 15 分钟

本文向您展示了在 Flutter 中实现完美的验证码输入框几种不同方法。

重点是什么?

真实世界的 完美的验证码输入框或 PIN 输入 UI 通常满足以下最低要求:

  • 有 4 个或 6 个文本域,每个文本域只能接受 1 个字符(通常是一个数字)

  • 输入数字后自动聚焦下一个字段

您经常在需要电话号码确认、电子邮件或双因素身份验证的应用程序中看到此功能。

从头开始制作 OTP 字段

应用预览



此示例创建一个简单的 OTP 屏幕。首先,聚焦第一个输入字段。当您输入一个数字时,光标将自动移动到下一个字段。当按下提交按钮时,您输入的 OTP 代码将显示在屏幕上。

以下是它的工作原理:

测试此应用程序时,您应该使用模拟器的软键盘而不是计算机的硬件键盘。


代码

创建一个名为 OtpInput 的可重用小部件:

// Create an input widget that takes only one digitclass OtpInput extends StatelessWidget {  final TextEditingController controller;  final bool autoFocus;  const OtpInput(this.controller, this.autoFocus, {Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return SizedBox(      height: 60,      width: 50,      child: TextField(        autofocus: autoFocus,        textAlign: TextAlign.center,        keyboardType: TextInputType.number,        controller: controller,        maxLength: 1,        cursorColor: Theme.of(context).primaryColor,        decoration: const InputDecoration(            border: OutlineInputBorder(),            counterText: '',            hintStyle: TextStyle(color: Colors.black, fontSize: 20.0)),        onChanged: (value) {          if (value.length == 1) {            FocusScope.of(context).nextFocus();          }        },      ),    );  }}
复制代码

main.dart 中的完整源代码和解释(我将 OtpInput 类放在文件底部):

import 'dart:math' as math;import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';import 'package:async/async.dart';import 'package:flutter/scheduler.dart';import 'package:url_strategy/url_strategy.dart';void main() {  setPathUrlStrategy();  runApp(MyApp());}class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return MaterialApp(      // Hide the debug banner      debugShowCheckedModeBanner: false,      title: '坚果',      theme: ThemeData(        primarySwatch: Colors.indigo,      ),      home: const HomeScreen(),    );  }}class HomeScreen extends StatefulWidget {  const HomeScreen({Key? key}) : super(key: key);  @override  State<HomeScreen> createState() => _HomeScreenState();}class _HomeScreenState extends State<HomeScreen> {  String _imageUrl =      'https://luckly007.oss-cn-beijing.aliyuncs.com/image/image-20211124085239175.png';  double _fontSize = 20;  String _title = "坚果公众号";  // 4 text editing controllers that associate with the 4 input fields  final TextEditingController _fieldOne = TextEditingController();  final TextEditingController _fieldTwo = TextEditingController();  final TextEditingController _fieldThree = TextEditingController();  final TextEditingController _fieldFour = TextEditingController();  // This is the entered code  // It will be displayed in a Text widget  String? _otp;  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(_title),      ),      body: Column(        mainAxisAlignment: MainAxisAlignment.center,        children: [          const Text('请输入验证码'),          const SizedBox(            height: 30,          ),          // Implement 4 input fields          Row(            mainAxisAlignment: MainAxisAlignment.spaceEvenly,            children: [              OtpInput(_fieldOne, true),              OtpInput(_fieldTwo, false),              OtpInput(_fieldThree, false),              OtpInput(_fieldFour, false)            ],          ),          const SizedBox(            height: 30,          ),          ElevatedButton(              onPressed: () {                setState(() {                  _otp = _fieldOne.text +                      _fieldTwo.text +                      _fieldThree.text +                      _fieldFour.text;                });              },              child: const Text('提交')),          const SizedBox(            height: 30,          ),          // Display the entered OTP code          Text(            _otp ?? '验证码',            style: const TextStyle(fontSize: 30),          )        ],      ),    );  }}// Create an input widget that takes only one digitclass OtpInput extends StatelessWidget {  final TextEditingController controller;  final bool autoFocus;  const OtpInput(this.controller, this.autoFocus, {Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return SizedBox(      height: 60,      width: 50,      child: TextField(        autofocus: autoFocus,        textAlign: TextAlign.center,        keyboardType: TextInputType.number,        controller: controller,        maxLength: 1,        cursorColor: Theme.of(context).primaryColor,        decoration: const InputDecoration(            border: OutlineInputBorder(),            counterText: '',            hintStyle: TextStyle(color: Colors.black, fontSize: 20.0)),        onChanged: (value) {          if (value.length == 1) {            FocusScope.of(context).nextFocus();          }        },      ),    );  }}
复制代码

使用第三个包

为了仅用几行代码快速实现您的目标,您可以使用第三方插件。在我们的例子中一些好的是pin_code_fieldsotp_text_field等。 下面的例子将使用 pin_code_fileds,它提供了很多很棒的功能:



  • 自动将下一个字段集中在打字上,将上一个字段集中在委派上

  • 可以设置为任意长度

  • 高度可定制

  • 输入文本的 3 种不同类型的动画

  • 动画活动、非活动、选定和禁用字段颜色切换

  • 自动对焦选项

  • 从剪贴板粘贴 OTP 代码

您还可以在终端窗口中看到您输入的字符:



代码

1.安装插件:

flutter pub add pin_code_fields
复制代码

2.最终代码:

import 'dart:math' as math;import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';import 'package:async/async.dart';import 'package:pin_code_fields/pin_code_fields.dart';import 'package:url_strategy/url_strategy.dart';void main() {  setPathUrlStrategy();  runApp(MyApp());}class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return MaterialApp(      // Hide the debug banner      debugShowCheckedModeBanner: false,      title: '坚果',      theme: ThemeData(        primarySwatch: Colors.indigo,      ),      home: const HomeScreen(),    );  }}class HomeScreen extends StatefulWidget {  const HomeScreen({Key? key}) : super(key: key);  @override  State<HomeScreen> createState() => _HomeScreenState();}class _HomeScreenState extends State<HomeScreen> {  String _imageUrl =      'https://luckly007.oss-cn-beijing.aliyuncs.com/image/image-20211124085239175.png';  double _fontSize = 20;  String _title = "坚果公众号";  // 4 text editing controllers that associate with the 4 input fields  TextEditingController textEditingController = TextEditingController();  String currentText = "";  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(_title),      ),      body: Padding(        padding: const EdgeInsets.all(30),        child: Center(          child: PinCodeTextField(            length: 6,            obscureText: false,            animationType: AnimationType.fade,            pinTheme: PinTheme(              shape: PinCodeFieldShape.box,              borderRadius: BorderRadius.circular(5),              fieldHeight: 50,              fieldWidth: 40,              activeFillColor: Colors.white,            ),            animationDuration: const Duration(milliseconds: 300),            backgroundColor: Colors.blue.shade50,            enableActiveFill: true,            controller: textEditingController,            onCompleted: (v) {              debugPrint("Completed");            },            onChanged: (value) {              debugPrint(value);              setState(() {                currentText = value;              });            },            beforeTextPaste: (text) {              return true;            },            appContext: context,          ),        ),      ),    );  }}
复制代码

结论

我们已经介绍了 2 个在 Flutter 中创建现代优雅的 完美的验证码输入框/PIN 输入字段的示例。

关于作者:

坚果,目前是华为云享专家,51CTO 博客首席体验官,专注于大前端技术的分享,包括 Flutter,小程序,安卓,VUE,JavaScript。公众号有更多细节。

发布于: 1 小时前
用户头像

坚果前端

关注

此间若无火炬,我便是唯一的光 2020.10.25 加入

公众号:“坚果前端”,华为云享专家,51CTO博客首席体验官,专注于大前端技术的分享,包括Flutter,小程序,安卓,VUE,JavaScript。

评论

发布
暂无评论
Flutter 完美的验证码输入框(2 种方法)【Flutter专题25】