pin_code_fields

一个 Flutter 包,可以帮助您生成 PIN 码字段。可用于 OTP 或 PIN 码输入。

功能 ?

  • 输入时自动聚焦到下一个字段,删除时自动聚焦到上一个字段
  • 可设置为任意长度(推荐3-6个字段)
  • 文本字段的3种不同形状
  • 高度可定制
  • 输入文本的3种不同类型的动画
  • 输入文本时颜色的动态切换(活动/非活动状态)
  • 自动聚焦选项
  • 从剪贴板粘贴OTP代码
  • iOS自动填充支持
  • 获取当前输入的文本并根据您的条件进行验证。(例如:if (currentText.length != 6 || currentText != "<您的目标代码>"))

属性

/// length of how many cells there should be. 4-6 is recommended by me
final int length;

/// you already know what it does i guess :P default is false
final bool obsecureText;

/// returns the current typed text in the fields
final ValueChanged<String> currentText;

/// this defines the shape of the input fields. Default is underlined
final PinCodeFieldShape shape;

/// the style of the text, default is [ fontSize: 20, color: Colors.black, fontWeight: FontWeight.bold]
final TextStyle textStyle;

/// background color for the whole row of pin code fields. Default is [Colors.white]
final Color backgroundColor;

/// Border radius of each pin code field
final BorderRadius borderRadius;

/// [height] for the pin code field. default is [50.0]
final double fieldHeight;

/// [width] for the pin code field. default is [40.0]
final double fieldWidth;

/// If the pin code field should be autofocused or not. Default is [false]
final bool autoFocus;

/// This defines how the elements in the pin code field align. Default to [MainAxisAlignment.spaceBetween]
final MainAxisAlignment mainAxisAlignment;

/// Colors of the input fields which have inputs. Default is [Colors.green]
final Color activeColor;

/// Colors of the input fields which don't have inputs. Default is [Colors.red]
final Color inactiveColor;

/// Border width for the each input fields. Default is [2.0]
final double borderWidth;

/// [AnimationType] for the text to appear in the pin code field. Default is [AnimationType.slide]
final AnimationType animationType;

/// Duration for the animation. Default is [Duration(milliseconds: 150)]
final Duration animationDuration;

/// [Curve] for the animation. Default is [Curves.easeInOut]
final Curve animationCurve;

/// [TextInputType] for the pin code fields. default is [TextInputType.visiblePassword]
final TextInputType textInputType;

Flare动画鸣谢: https://www.2dimensions.com/a/atiq31416/files/flare/otp-verification/preview

入门

演示

pin_code

android_paste

ios_pasting

不同的形状

pic1

pic2

pic3

密码文本字段小部件示例

PinCodeTextField(
  length: 6,
  obsecureText: false,
  animationType: AnimationType.fade,
  shape: PinCodeFieldShape.box,
  animationDuration: Duration(milliseconds: 300),
  borderRadius: BorderRadius.circular(5),
  fieldHeight: 50,
  fieldWidth: 40,
  currentText: (value) {
    setState(() {
      currentText = value;
    });
  },
)

形状可以是以下3种类型之一

enum PinCodeFieldShape { box, underline, circle }

动画可以是以下3种类型之一

enum AnimationType { scale, slide, fade, none }

此完整代码来自示例文件夹。您可以运行示例进行查看。

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: PinCodeVerificationScreen(
          "+8801376221100"), // a random number, please don't call xD
    );
  }
}

class PinCodeVerificationScreen extends StatefulWidget {
  final String phoneNumber;
  PinCodeVerificationScreen(this.phoneNumber);
  @override
  _PinCodeVerificationScreenState createState() =>
      _PinCodeVerificationScreenState();
}

class _PinCodeVerificationScreenState extends State<PinCodeVerificationScreen> {
  var onTapRecognizer;

  /// this [StreamController] will take input of which function should be called

  bool hasError = false;
  String currentText = "";
  final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
  @override
  void initState() {
    onTapRecognizer = TapGestureRecognizer()
      ..onTap = () {
        Navigator.pop(context);
      };

    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: scaffoldKey,
      body: GestureDetector(
        onTap: () {
          FocusScope.of(context).requestFocus(new FocusNode());
        },
        child: Container(
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          child: ListView(
            children: <Widget>[
              SizedBox(height: 30),
              Image.asset(
                'assets/verify.png',
                height: MediaQuery.of(context).size.height / 3,
                fit: BoxFit.fitHeight,
              ),
              SizedBox(height: 8),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: Text(
                  'Phone Number Verification',
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
                  textAlign: TextAlign.center,
                ),
              ),
              Padding(
                padding:
                    const EdgeInsets.symmetric(horizontal: 30.0, vertical: 8),
                child: RichText(
                  text: TextSpan(
                      text: "Enter the code sent to ",
                      children: [
                        TextSpan(
                            text: widget.phoneNumber,
                            style: TextStyle(
                                color: Colors.black,
                                fontWeight: FontWeight.bold,
                                fontSize: 15)),
                      ],
                      style: TextStyle(color: Colors.black54, fontSize: 15)),
                  textAlign: TextAlign.center,
                ),
              ),
              SizedBox(
                height: 20,
              ),
              Padding(
                  padding:
                      const EdgeInsets.symmetric(vertical: 8.0, horizontal: 30),
                  child: PinCodeTextField(
                    length: 6,
                    obsecureText: false,
                    animationType: AnimationType.fade,
                    shape: PinCodeFieldShape.underline,
                    animationDuration: Duration(milliseconds: 300),
                    borderRadius: BorderRadius.circular(5),
                    fieldHeight: 50,
                    fieldWidth: 40,
                    currentText: (value) {
                      setState(() {
                        currentText = value;
                      });
                    },
                  )),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 30.0),
                // error showing widget
                child: Text(
                  hasError ? "*Please fill up all the cells properly" : "",
                  style: TextStyle(color: Colors.red.shade300, fontSize: 15),
                ),
              ),
              SizedBox(
                height: 20,
              ),
              RichText(
                textAlign: TextAlign.center,
                text: TextSpan(
                    text: "Didn't receive the code? ",
                    style: TextStyle(color: Colors.black54, fontSize: 15),
                    children: [
                      TextSpan(
                          text: " RESEND",
                          recognizer: onTapRecognizer,
                          style: TextStyle(
                              color: Color(0xFF91D3B3),
                              fontWeight: FontWeight.bold,
                              fontSize: 16))
                    ]),
              ),
              SizedBox(
                height: 14,
              ),
              Container(
                margin:
                    const EdgeInsets.symmetric(vertical: 16.0, horizontal: 30),
                child: ButtonTheme(
                  height: 50,
                  child: FlatButton(
                    onPressed: () {
                      // conditions for validating
                      if (currentText.length != 6 || currentText != "towtow") {
                        setState(() {
                          hasError = true;
                        });
                      } else {
                        setState(() {
                          hasError = false;
                          scaffoldKey.currentState.showSnackBar(SnackBar(
                            content: Text("Aye!!"),
                            duration: Duration(seconds: 2),
                          ));
                        });
                      }
                    },
                    child: Center(
                        child: Text(
                      "VERIFY".toUpperCase(),
                      style: TextStyle(
                          color: Colors.white,
                          fontSize: 18,
                          fontWeight: FontWeight.bold),
                    )),
                  ),
                ),
                decoration: BoxDecoration(
                    color: Colors.green.shade300,
                    borderRadius: BorderRadius.circular(5),
                    boxShadow: [
                      BoxShadow(
                          color: Colors.green.shade200,
                          offset: Offset(1, -2),
                          blurRadius: 5),
                      BoxShadow(
                          color: Colors.green.shade200,
                          offset: Offset(-1, 2),
                          blurRadius: 5)
                    ]),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

GitHub

https://github.com/adar2378/pin_code_fields