📜  Flutter – OTP 输入字段

📅  最后修改于: 2022-05-13 01:54:18.533000             🧑  作者: Mango

Flutter – OTP 输入字段

主要需要 OTP 验证的现代应用程序需要 OTP 样式的输入字段。我们需要生成更大的样板代码来创建惊人的输入字段。然后, pinput包来救援。使用此软件包,我们可以轻松创建可自定义的 OTP 输入字段。让我们在本文中看看它的实现。

第一步:添加依赖

要使用pinput ,我们需要在应用程序的pubspec.yaml文件中添加pinput

flutter pub add pinput

第二步:导入依赖

在我们需要创建输入字段的文件中导入依赖项。

Dart
import 'package:pinput/pin_put/pin_put.dart';


Dart
void _showSnackBar(String pin) {
    final snackBar = SnackBar(
      duration: Duration(seconds: 4),
      content: Container(
        height: 80.0,
        child: Center(
          child: Text(
            'Pin Submitted: $pin',
            style: TextStyle(fontSize: 25.0),
          ),
        ),
      ),
      backgroundColor: Colors.green,
    );
    ScaffoldMessenger.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(snackBar);
  }


Dart
final _pinPutController = TextEditingController();


Dart
PinPut({
  Key? key,
  required int fieldsCount,
  void Function(String)? onSubmit,
  void Function(String?)? onSaved,
  void Function(String)? onChanged,
  void Function()? onTap,
  void Function(String?)? onClipboardFound,
  TextEditingController? controller,
  FocusNode? focusNode,
  Widget? preFilledWidget,
  List separatorPositions = const [],
  Widget separator = const SizedBox(width: 15.0),
  TextStyle? textStyle,
  BoxDecoration? submittedFieldDecoration,
  BoxDecoration? selectedFieldDecoration,
  BoxDecoration? followingFieldDecoration,
  BoxDecoration? disabledDecoration,
  double? eachFieldWidth,
  double? eachFieldHeight,
  MainAxisAlignment fieldsAlignment = MainAxisAlignment.spaceBetween,
  AlignmentGeometry eachFieldAlignment = Alignment.center,
  EdgeInsetsGeometry? eachFieldMargin,
  EdgeInsetsGeometry? eachFieldPadding,
  BoxConstraints eachFieldConstraints = const BoxConstraints(minHeight: 40.0,
                                                             minWidth: 40.0),
  InputDecoration? inputDecoration,
  Curve animationCurve = Curves.linear,
  Duration animationDuration = const Duration(milliseconds: 160),
  PinAnimationType pinAnimationType = PinAnimationType.slide,
  Offset? slideTransitionBeginOffset,
  bool enabled = true,
  bool checkClipboard = false,
  bool useNativeKeyboard = true,
  bool autofocus = false,
  AutovalidateMode autovalidateMode = AutovalidateMode.disabled,
  bool withCursor = false,
  Widget? cursor,
  Brightness? keyboardAppearance,
  List? inputFormatters,
  String? Function(String?)? validator,
  TextInputType keyboardType = TextInputType.number,
  String? obscureText,
  TextCapitalization textCapitalization = TextCapitalization.none,
  TextInputAction? textInputAction,
  ToolbarOptions? toolbarOptions = const ToolbarOptions(paste: true),
  MainAxisSize mainAxisSize = MainAxisSize.max,
  Iterable? autofillHints,
  bool enableIMEPersonalizedLearning = true,
  String? initialValue,
  SmartDashesType? smartDashesType,
  SmartQuotesType? smartQuotesType,
  bool enableSuggestions = true,
  MaxLengthEnforcement? maxLengthEnforcement,
  void Function()? onEditingComplete,
  double cursorWidth = 2,
  double? cursorHeight,
  Radius? cursorRadius,
  Color? cursorColor,
  bool enableInteractiveSelection = true,
  TextSelectionControls? selectionControls,
  Widget? Function(BuildContext, {required int currentLength,
                                  required bool isFocused,
                                  required int? maxLength})? buildCounter,
  String? restorationId
  })


Dart
Widget darkRoundedPinPut() {
    return PinPut(
      eachFieldWidth: 50.0,
      eachFieldHeight: 50.0,
      withCursor: true,
      fieldsCount: 5,
      controller: _pinPutController,
      eachFieldMargin: EdgeInsets.symmetric(horizontal: 10),
      onSubmit: (String pin) => _showSnackBar(pin),
      submittedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      selectedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      followingFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      pinAnimationType: PinAnimationType.rotation,
      textStyle: TextStyle(color: Colors.white,
                           fontSize: 20.0,
                           height: 1),
    );
  }


Dart
Widget animatedBorders() {
  return Padding(
    padding: const EdgeInsets.all(8.0),
    child: PinPut(
      fieldsCount: 4,
      eachFieldHeight: 50.0,
      withCursor: true,
      onSubmit: (String pin) => _showSnackBar(pin),
      controller: _pinPutController,
      submittedFieldDecoration: BoxDecoration(
        border: Border.all(color: Colors.black),
        borderRadius: BorderRadius.circular(15.0),
      ).copyWith(
        borderRadius: BorderRadius.circular(20.0),
      ),
      selectedFieldDecoration: BoxDecoration(
        color: Colors.green,
        border: Border.all(color: Colors.black),
        borderRadius: BorderRadius.circular(15.0),
      ),
      followingFieldDecoration: BoxDecoration(
        border: Border.all(color: Colors.black),
        borderRadius: BorderRadius.circular(15.0),
      ).copyWith(
        borderRadius: BorderRadius.circular(5.0),
        border: Border.all(
          color: Colors.black,
        ),
      ),
    ),
  );
}


Dart
import 'package:flutter/material.dart';
import 'package:pinput/pin_put/pin_put.dart';
  
void main() => runApp(MyApp());
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.green),
      home: PinPutView(),
    );
  }
}
  
class PinPutView extends StatefulWidget {
  @override
  PinPutViewState createState() => PinPutViewState();
}
  
class PinPutViewState extends State {
  final _pinPutController = TextEditingController();
  final _pinPutController2 = TextEditingController();
  
  @override
  void initState() {
    super.initState();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("GeeksForGeeks"),
          centerTitle: true,
        ),
        body: Container(
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(child: darkRoundedPinPut()),
                Expanded(child: animatedBorders())
              ]),
        ));
  }
  
  Widget darkRoundedPinPut() {
    return PinPut(
      eachFieldWidth: 50.0,
      eachFieldHeight: 50.0,
      withCursor: true,
      fieldsCount: 5,
      controller: _pinPutController,
      eachFieldMargin: EdgeInsets.symmetric(horizontal: 10),
      onSubmit: (String pin) => _showSnackBar(pin),
      submittedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      selectedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      followingFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      pinAnimationType: PinAnimationType.rotation,
      textStyle: TextStyle(color: Colors.white,
                           fontSize: 20.0,
                           height: 1),
    );
  }
  
  Widget animatedBorders() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: PinPut(
        fieldsCount: 4,
        eachFieldHeight: 50.0,
        withCursor: true,
        onSubmit: (String pin) => _showSnackBar(pin),
        controller: _pinPutController2,
        submittedFieldDecoration: BoxDecoration(
          border: Border.all(color: Colors.black),
          borderRadius: BorderRadius.circular(15.0),
        ).copyWith(
          borderRadius: BorderRadius.circular(20.0),
        ),
        selectedFieldDecoration: BoxDecoration(
          color: Colors.green,
          border: Border.all(color: Colors.black),
          borderRadius: BorderRadius.circular(15.0),
        ),
        followingFieldDecoration: BoxDecoration(
          border: Border.all(color: Colors.black),
          borderRadius: BorderRadius.circular(15.0),
        ).copyWith(
          borderRadius: BorderRadius.circular(5.0),
          border: Border.all(
            color: Colors.black,
          ),
        ),
      ),
    );
  }
  
  void _showSnackBar(String pin) {
    final snackBar = SnackBar(
      duration: Duration(seconds: 4),
      content: Container(
        height: 80.0,
        child: Center(
          child: Text(
            'Pin Submitted: $pin',
            style: TextStyle(fontSize: 25.0),
          ),
        ),
      ),
      backgroundColor: Colors.green,
    );
    ScaffoldMessenger.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(snackBar);
  }
}


第 3 步:实施

  • 创建一个 Snackbar,在提交 pin 时显示 pin。

Dart

void _showSnackBar(String pin) {
    final snackBar = SnackBar(
      duration: Duration(seconds: 4),
      content: Container(
        height: 80.0,
        child: Center(
          child: Text(
            'Pin Submitted: $pin',
            style: TextStyle(fontSize: 25.0),
          ),
        ),
      ),
      backgroundColor: Colors.green,
    );
    ScaffoldMessenger.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(snackBar);
  }
  • 初始化一个 TextEditingController() _pinputController。

Dart

final _pinPutController = TextEditingController();
  • 我们可以使用PinPut()小部件创建输入字段。下面给出了PinPut() 的所有属性。

Dart

PinPut({
  Key? key,
  required int fieldsCount,
  void Function(String)? onSubmit,
  void Function(String?)? onSaved,
  void Function(String)? onChanged,
  void Function()? onTap,
  void Function(String?)? onClipboardFound,
  TextEditingController? controller,
  FocusNode? focusNode,
  Widget? preFilledWidget,
  List separatorPositions = const [],
  Widget separator = const SizedBox(width: 15.0),
  TextStyle? textStyle,
  BoxDecoration? submittedFieldDecoration,
  BoxDecoration? selectedFieldDecoration,
  BoxDecoration? followingFieldDecoration,
  BoxDecoration? disabledDecoration,
  double? eachFieldWidth,
  double? eachFieldHeight,
  MainAxisAlignment fieldsAlignment = MainAxisAlignment.spaceBetween,
  AlignmentGeometry eachFieldAlignment = Alignment.center,
  EdgeInsetsGeometry? eachFieldMargin,
  EdgeInsetsGeometry? eachFieldPadding,
  BoxConstraints eachFieldConstraints = const BoxConstraints(minHeight: 40.0,
                                                             minWidth: 40.0),
  InputDecoration? inputDecoration,
  Curve animationCurve = Curves.linear,
  Duration animationDuration = const Duration(milliseconds: 160),
  PinAnimationType pinAnimationType = PinAnimationType.slide,
  Offset? slideTransitionBeginOffset,
  bool enabled = true,
  bool checkClipboard = false,
  bool useNativeKeyboard = true,
  bool autofocus = false,
  AutovalidateMode autovalidateMode = AutovalidateMode.disabled,
  bool withCursor = false,
  Widget? cursor,
  Brightness? keyboardAppearance,
  List? inputFormatters,
  String? Function(String?)? validator,
  TextInputType keyboardType = TextInputType.number,
  String? obscureText,
  TextCapitalization textCapitalization = TextCapitalization.none,
  TextInputAction? textInputAction,
  ToolbarOptions? toolbarOptions = const ToolbarOptions(paste: true),
  MainAxisSize mainAxisSize = MainAxisSize.max,
  Iterable? autofillHints,
  bool enableIMEPersonalizedLearning = true,
  String? initialValue,
  SmartDashesType? smartDashesType,
  SmartQuotesType? smartQuotesType,
  bool enableSuggestions = true,
  MaxLengthEnforcement? maxLengthEnforcement,
  void Function()? onEditingComplete,
  double cursorWidth = 2,
  double? cursorHeight,
  Radius? cursorRadius,
  Color? cursorColor,
  bool enableInteractiveSelection = true,
  TextSelectionControls? selectionControls,
  Widget? Function(BuildContext, {required int currentLength,
                                  required bool isFocused,
                                  required int? maxLength})? buildCounter,
  String? restorationId
  })

让我们通过示例来看看两种不同风格的输入字段。

DarkRounded 字段:

Dart

Widget darkRoundedPinPut() {
    return PinPut(
      eachFieldWidth: 50.0,
      eachFieldHeight: 50.0,
      withCursor: true,
      fieldsCount: 5,
      controller: _pinPutController,
      eachFieldMargin: EdgeInsets.symmetric(horizontal: 10),
      onSubmit: (String pin) => _showSnackBar(pin),
      submittedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      selectedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      followingFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      pinAnimationType: PinAnimationType.rotation,
      textStyle: TextStyle(color: Colors.white,
                           fontSize: 20.0,
                           height: 1),
    );
  }

输出:

带有 AnimatedBorder 的字段:

Dart

Widget animatedBorders() {
  return Padding(
    padding: const EdgeInsets.all(8.0),
    child: PinPut(
      fieldsCount: 4,
      eachFieldHeight: 50.0,
      withCursor: true,
      onSubmit: (String pin) => _showSnackBar(pin),
      controller: _pinPutController,
      submittedFieldDecoration: BoxDecoration(
        border: Border.all(color: Colors.black),
        borderRadius: BorderRadius.circular(15.0),
      ).copyWith(
        borderRadius: BorderRadius.circular(20.0),
      ),
      selectedFieldDecoration: BoxDecoration(
        color: Colors.green,
        border: Border.all(color: Colors.black),
        borderRadius: BorderRadius.circular(15.0),
      ),
      followingFieldDecoration: BoxDecoration(
        border: Border.all(color: Colors.black),
        borderRadius: BorderRadius.circular(15.0),
      ).copyWith(
        borderRadius: BorderRadius.circular(5.0),
        border: Border.all(
          color: Colors.black,
        ),
      ),
    ),
  );
}

输出:

完整源代码:

Dart

import 'package:flutter/material.dart';
import 'package:pinput/pin_put/pin_put.dart';
  
void main() => runApp(MyApp());
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.green),
      home: PinPutView(),
    );
  }
}
  
class PinPutView extends StatefulWidget {
  @override
  PinPutViewState createState() => PinPutViewState();
}
  
class PinPutViewState extends State {
  final _pinPutController = TextEditingController();
  final _pinPutController2 = TextEditingController();
  
  @override
  void initState() {
    super.initState();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("GeeksForGeeks"),
          centerTitle: true,
        ),
        body: Container(
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(child: darkRoundedPinPut()),
                Expanded(child: animatedBorders())
              ]),
        ));
  }
  
  Widget darkRoundedPinPut() {
    return PinPut(
      eachFieldWidth: 50.0,
      eachFieldHeight: 50.0,
      withCursor: true,
      fieldsCount: 5,
      controller: _pinPutController,
      eachFieldMargin: EdgeInsets.symmetric(horizontal: 10),
      onSubmit: (String pin) => _showSnackBar(pin),
      submittedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      selectedFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      followingFieldDecoration: BoxDecoration(
        color: Colors.green[800],
        borderRadius: BorderRadius.circular(15.0),
      ),
      pinAnimationType: PinAnimationType.rotation,
      textStyle: TextStyle(color: Colors.white,
                           fontSize: 20.0,
                           height: 1),
    );
  }
  
  Widget animatedBorders() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: PinPut(
        fieldsCount: 4,
        eachFieldHeight: 50.0,
        withCursor: true,
        onSubmit: (String pin) => _showSnackBar(pin),
        controller: _pinPutController2,
        submittedFieldDecoration: BoxDecoration(
          border: Border.all(color: Colors.black),
          borderRadius: BorderRadius.circular(15.0),
        ).copyWith(
          borderRadius: BorderRadius.circular(20.0),
        ),
        selectedFieldDecoration: BoxDecoration(
          color: Colors.green,
          border: Border.all(color: Colors.black),
          borderRadius: BorderRadius.circular(15.0),
        ),
        followingFieldDecoration: BoxDecoration(
          border: Border.all(color: Colors.black),
          borderRadius: BorderRadius.circular(15.0),
        ).copyWith(
          borderRadius: BorderRadius.circular(5.0),
          border: Border.all(
            color: Colors.black,
          ),
        ),
      ),
    );
  }
  
  void _showSnackBar(String pin) {
    final snackBar = SnackBar(
      duration: Duration(seconds: 4),
      content: Container(
        height: 80.0,
        child: Center(
          child: Text(
            'Pin Submitted: $pin',
            style: TextStyle(fontSize: 25.0),
          ),
        ),
      ),
      backgroundColor: Colors.green,
    );
    ScaffoldMessenger.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(snackBar);
  }
}

输出: