pinput 2.2.14 icon indicating copy to clipboard operation
pinput: ^2.2.14 copied to clipboard

Pin code input (OTP) text field, iOS SMS autofill, Android SMS autofill One Time Code, Password, Passcode, Captcha, Security, Coupon, Wowcher, 2FA, Two step verification

Flutter Pinput From Tornike & Great Contributors

Pub package Github starts style: effective dart pub package

Flutter package to create easily customizable Pin code input field, that your designers can't even draw in Figma 🤭

Please see the Changelog if you are migrating from version < 2.0.0

Bonus tip: 🤫 Tell your PM that you need a week to implement the feature and chill with your friends meanwhile.

Features: #

  • Animated Decoration Switching
  • Form validation
  • SMS Autofill on iOS
  • SMS Autofill on Android
  • Standard Cursor
  • Custom Cursor
  • Cursor Animation
  • Copy From Clipboard
  • Ready For Custom Keyboard
  • Standard Paste option
  • Obscuring Character
  • Obscuring Widget
  • Haptic Feedback
  • Close Keyboard After Completion
  • Beautiful Examples

Support #

PRs Welcome

Discord Channel

Examples app on Github has multiple templates to choose from

Don't forget to give it a star ⭐

Demo #

Live DemoRounded With ShadowsRounded With Cursor
Live DemoRounded With ShadowsRounded With Cursor
Rounded FilledWith Bottom CursorFilled
Rounded FilledWith Bottom CursorFilled

Getting Started #

The pin has 6 states default focused, submitted, following, disabled, error, you can customize each state by specifying theme parameter. Pin smoothly animates from one state to another automatically. PinTheme Class

PropertyDefault/Type
width56.0
height60.0
textStyleTextStyle()
marginEdgeInsetsGeometry
paddingEdgeInsetsGeometry
constraintsBoxConstraints

You can use standard Pinput like so

  Widget buildPinPut() {
    return Pinput(
      onCompleted: (pin) => print(pin),
    );
  }

If you want to customize it, create defaultPinTheme first.

    final defaultPinTheme = PinTheme(
      width: 56,
      height: 56,
      textStyle: TextStyle(fontSize: 20, color: Color.fromRGBO(30, 60, 87, 1), fontWeight: FontWeight.w600),
      decoration: BoxDecoration(
        border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
        borderRadius: BorderRadius.circular(20),
      ),
    );

if you want all pins to be the same don't pass other theme parameters, If not, create focusedPinTheme, submittedPinTheme, followingPinTheme, errorPinTheme from defaultPinTheme

    final focusedPinTheme = defaultPinTheme.copyDecorationWith(
      border: Border.all(color: Color.fromRGBO(114, 178, 238, 1)),
      borderRadius: BorderRadius.circular(8),
    );

    final submittedPinTheme = defaultPinTheme.copyWith(
      decoration: defaultPinTheme.decoration.copyWith(
        color: Color.fromRGBO(234, 239, 243, 1),
      ),
    );

Put everything together

    final defaultPinTheme = PinTheme(
      width: 56,
      height: 56,
      textStyle: TextStyle(fontSize: 20, color: Color.fromRGBO(30, 60, 87, 1), fontWeight: FontWeight.w600),
      decoration: BoxDecoration(
        border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
        borderRadius: BorderRadius.circular(20),
      ),
    );

    final focusedPinTheme = defaultPinTheme.copyDecorationWith(
      border: Border.all(color: Color.fromRGBO(114, 178, 238, 1)),
      borderRadius: BorderRadius.circular(8),
    );

    final submittedPinTheme = defaultPinTheme.copyWith(
      decoration: defaultPinTheme.decoration.copyWith(
        color: Color.fromRGBO(234, 239, 243, 1),
      ),
    );

    return Pinput(
      defaultPinTheme: defaultPinTheme,
      focusedPinTheme: focusedPinTheme,
      submittedPinTheme: submittedPinTheme,
      validator: (s) {
        return s == '2222' ? null : 'Pin is incorrect';
      },
      pinputAutovalidateMode: PinputAutovalidateMode.onSubmit,
      showCursor: true,
      onCompleted: (pin) => print(pin),
    );

SMS Autofill #

iOS #

Works out of the box, by tapping the code on top of the keyboard

Android #

If you are using firebase_auth you don't need to use any option below, because firebase_auth support automatic SMS code resolution.

You have two options, SMS Retriever API and SMS User Consent API,

SmartAuth is a wrapper package for Flutter for these APIs and it is behind the autofill support of Pinput

SMS Retriever API

To use Retriever API you need The App signature, Pinput calculates the hash for you and prints it in the console Sms code will be automatically applied, without user interaction.

Note that The App Signature might be different for debug and release mode

    return Pinput(
      androidSmsAutofillMethod:  AndroidSmsAutofillMethod.smsRetrieverApi,
    );

Example of printed signature Pinput: App Signature for SMS Retriever API Is: kg+TZ3A5qzS

You don't need the App signature, the user will be prompted to confirm reading the message

    return Pinput(
      androidSmsAutofillMethod:  AndroidSmsAutofillMethod.smsUserConsentApi,
    );
Request Hint

SmartAuth #

If autofill support doesn't fit your needs, you can use SmartAuth to implement autofill, Also, you can suggest a phone number by showing native Android dialog.

No need to add SmartAuth dependency, it is already added

See Example app for more templates #

Tips #

  • Controller

    /// Create Controller  
    final pinController = TextEditingController();  
  
    /// Set text programmatically  
    pinController.setText('1222');  
  
    /// Append typed character, useful if you are using custom keyboard  
    pinController.append('1', 4);  
  
    /// Delete last character  
    pinController.delete();  
  
    /// Don't call setText, append, delete in build method, this is just illustration.  
  
    return Pinput(  
      controller: pinController,  
    );  
  • Focus

    /// Create FocusNode  
    final pinputFocusNode = FocusNode();  
  
    /// Focus pinput  
    pinputFocusNode.requestFocus();  
  
    /// UnFocus pinput  
    pinputFocusNode.unfocus();  
  
    /// Don't call requestFocus, unfocus in build method, this is just illustration.  
  
    return Pinput(  
      focusNode: pinputFocusNode,  
    );  
  • Validation

    /// Create key
    final formKey = GlobalKey<FormState>();

    /// Validate manually
    /// Don't call validate in build method, this is just illustration.
    formKey.currentState!.validate();

    return Form(
      key: formKey,
      child: Pinput(
        // Without Validator
        // If true error state will be applied no matter what validator returns
        forceErrorState: true,
        // Text will be displayed under the Pinput
        errorText: 'Error',

        /// ------------
        /// With Validator
        /// Auto validate after user tap on keyboard done button, or completes Pinput
        pinputAutovalidateMode: PinputAutovalidateMode.onSubmit,
        validator: (pin) {
          if (pin == '2224') return null;

          /// Text will be displayed under the Pinput
          return 'Pin is incorrect';
        },
      ),
    );

Properties #

  /// Theme of the pin in default state
  final PinTheme? defaultPinTheme;

  /// Theme of the pin in focused state
  final PinTheme? focusedPinTheme;

  /// Theme of the pin in submitted state
  final PinTheme? submittedPinTheme;

  /// Theme of the pin in following state
  final PinTheme? followingPinTheme;

  /// Theme of the pin in disabled state
  final PinTheme? disabledPinTheme;

  /// Theme of the pin in error state
  final PinTheme? errorPinTheme;

  /// If true keyboard will be closed
  final bool closeKeyboardWhenCompleted;

  /// Displayed fields count. PIN code length.
  final int length;

  /// By default Android autofill is Disabled, you cane enable it by using any of options listed below
  ///
  /// First option is [AndroidSmsAutofillMethod.smsRetrieverApi] it automatically reads sms without user interaction
  /// More about Sms Retriever API [https://developers.google.com/identity/sms-retriever/overview?hl=en]
  ///
  /// Second option requires user interaction to confirm reading a SMS, See readme for more details
  /// [AndroidSmsAutofillMethod.smsUserConsentApi]
  /// More about SMS User Consent API [https://developers.google.com/identity/sms-retriever/user-consent/overview]
  final AndroidSmsAutofillMethod androidSmsAutofillMethod;

  /// If true [androidSmsAutofillMethod] is not [AndroidSmsAutofillMethod.none]
  /// Pinput will listen multiple sms codes, helpful if user request another sms code
  final bool listenForMultipleSmsOnAndroid;

  /// Used to extract code from SMS for Android Autofill if [androidSmsAutofillMethod] is enabled
  /// By default it is [_defaultSmsCodeMatcher]
  final String? smsCodeMatcher;

  /// Fires when user completes pin input
  final ValueChanged<String>? onCompleted;

  /// Called every time input value changes.
  final ValueChanged<String>? onChanged;

  /// See [EditableText.onSubmitted]
  final ValueChanged<String>? onSubmitted;

  /// Called when user clicks on PinPut
  final VoidCallback? onTap;

  /// In order to catch event [enableInteractiveSelection] should be false
  final VoidCallback? onLongPress;

  /// Used to get, modify PinPut value and more.
  /// Don't forget to dispose controller
  /// ``` dart
  ///   @override
  ///   void dispose() {
  ///     controller.dispose();
  ///     super.dispose();
  ///   }
  /// ```
  final TextEditingController? controller;

  /// Defines the keyboard focus for this
  /// To give the keyboard focus to this widget, provide a [focusNode] and then
  /// use the current [FocusScope] to request the focus:
  /// Don't forget to dispose focusNode
  /// ``` dart
  ///   @override
  ///   void dispose() {
  ///     focusNode.dispose();
  ///     super.dispose();
  ///   }
  /// ```
  final FocusNode? focusNode;

  /// Widget that is displayed before field submitted.
  final Widget? preFilledWidget;

  /// Sets the positions where the separator should be shown
  final List<int>? separatorPositions;

  /// Builds a Pinput separator
  final Widget? separator;

  /// Defines how [Pinput] fields are being placed inside [Row]
  final MainAxisAlignment mainAxisAlignment;

  /// Defines how [Pinput] and ([errorText] or [errorBuilder]) are being placed inside [Column]
  final CrossAxisAlignment crossAxisAlignment;

  /// Defines how each [Pinput] field are being placed within the container
  final AlignmentGeometry pinContentAlignment;

  /// curve of every [Pinput] Animation
  final Curve animationCurve;

  /// Duration of every [Pinput] Animation
  final Duration animationDuration;

  /// Animation Type of each [Pinput] field
  /// options:
  /// none, scale, fade, slide, rotation
  final PinAnimationType pinAnimationType;

  /// Begin Offset of ever [Pinput] field when [pinAnimationType] is slide
  final Offset? slideTransitionBeginOffset;

  /// Defines [Pinput] state
  final bool enabled;

  /// See [EditableText.readOnly]
  final bool readOnly;

  /// See [EditableText.autofocus]
  final bool autofocus;

  /// Whether to use Native keyboard or custom one
  /// when flag is set to false [Pinput] wont be focusable anymore
  /// so you should set value of [Pinput]'s [TextEditingController] programmatically
  final bool useNativeKeyboard;

  /// If true, paste button will appear on longPress event
  final bool toolbarEnabled;

  /// Whether show cursor or not
  /// Default cursor '|' or [cursor]
  final bool showCursor;

  /// If [showCursor] true the focused field will show passed Widget
  final Widget? cursor;

  /// The appearance of the keyboard.
  /// This setting is only honored on iOS devices.
  /// If unset, defaults to [ThemeData.brightness].
  final Brightness? keyboardAppearance;

  /// See [EditableText.inputFormatters]
  final List<TextInputFormatter> inputFormatters;

  /// See [EditableText.keyboardType]
  final TextInputType keyboardType;

  /// Provide any symbol to obscure each [Pinput] pin
  /// Recommended ●
  final String obscuringCharacter;

  /// IF [obscureText] is true typed text will be replaced with passed Widget
  final Widget? obscuringWidget;

  /// Whether hide typed pin or not
  final bool obscureText;

  /// See [EditableText.textCapitalization]
  final TextCapitalization textCapitalization;

  /// The type of action button to use for the keyboard.
  ///
  /// Defaults to [TextInputAction.newline] if [keyboardType] is
  /// [TextInputType.multiline] and [TextInputAction.done] otherwise.
  final TextInputAction? textInputAction;

  /// Configuration of toolbar options.
  ///
  /// If not set, select all and paste will default to be enabled. Copy and cut
  /// will be disabled if [obscureText] is true. If [readOnly] is true,
  /// paste and cut will be disabled regardless.
  final ToolbarOptions toolbarOptions;

  /// See [EditableText.autofillHints]
  final Iterable<String>? autofillHints;

  /// See [EditableText.enableSuggestions]
  final bool enableSuggestions;

  /// See [EditableText.selectionControls]
  final TextSelectionControls? selectionControls;

  /// See [TextField.restorationId]
  final String? restorationId;

  /// Fires when clipboard has text of Pinput's length
  final ValueChanged<String>? onClipboardFound;

  /// Use haptic feedback everytime user types on keyboard
  /// See more details in [HapticFeedback]
  final HapticFeedbackType hapticFeedbackType;

  /// See [EditableText.onAppPrivateCommand]
  final AppPrivateCommandCallback? onAppPrivateCommand;

  /// See [EditableText.mouseCursor]
  final MouseCursor? mouseCursor;

  /// If true [errorPinTheme] will be applied and [errorText] will be displayed under the Pinput
  final bool forceErrorState;

  /// Text displayed under the Pinput if Pinput is invalid
  final String? errorText;

  /// Style of error text
  final TextStyle? errorTextStyle;

  /// If [showError] is true and [errorBuilder] is passed it will be rendered under the Pinput
  final PinputErrorBuilder? errorBuilder;

  /// Return null if pin is valid or any String otherwise
  final FormFieldValidator<String>? validator;

  /// Return null if pin is valid or any String otherwise
  final PinputAutovalidateMode pinputAutovalidateMode;
1128
likes
130
pub points
99%
popularity

Publisher

verified publisher iconfman.ge

Pin code input (OTP) text field, iOS SMS autofill, Android SMS autofill One Time Code, Password, Passcode, Captcha, Security, Coupon, Wowcher, 2FA, Two step verification

Repository (GitHub)

License

Icon for licenses.MIT (LICENSE)

Dependencies

flutter, smart_auth

More

Packages that depend on pinput