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);
}
}
输出: