Open In App

Flutter – OTP Input Fields

Last Updated : 08 Feb, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

Modern applications majorly that require OTP verification require OTP style input fields. We need to generate larger boilerplate code for creating amazing input fields. Then, the pinput package comes to the rescue. With this package, we can create customizable OTP input fields easily. Let us see its implementation in this article.

Step 1: Add the dependency

To use pinput, we need to add pinput in pubspec.yaml file of the app.

flutter pub add pinput

Step 2: Import the dependency

Import the dependency in the file where we need to create input fields.

Dart




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


Step 3: Implementation

  • Create a Snackbar that will show a pin when the pin is submitted.

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);
  }


  • Initialize a TextEditingController() _pinputController.

Dart




final _pinPutController = TextEditingController();


  • We can create input fields using PinPut() widget. Given below are all the properties of 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<int> 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<TextInputFormatter>? 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<String>? 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
  })


Let us see two different styles of input fields through examples.

DarkRounded Fields:

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),
    );
  }


Output:

Fields with 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,
        ),
      ),
    ),
  );
}


Output:

Full Source Code:

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<PinPutView> {
  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);
  }
}


Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads