pinput

7.0.0

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

Download archive

Readme

Pub package Github starts style: effective dart pub package

Flutter Pinput is a package that provides an easy-to-use and customizable Pin code input field. It offers several features such as animated decoration switching, form validation, SMS autofill, custom cursor, copying from clipboard, and more. It also provides beautiful examples that you can choose from.

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 Demo Rounded With Shadows Rounded With Cursor
Rounded Filled With Bottom Cursor Filled

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

Property Default/Type
width 56.0
height 60.0
textStyle TextStyle()
margin EdgeInsetsGeometry
padding EdgeInsetsGeometry
constraints BoxConstraints

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 have to set controller's value in verificationCompleted callback, here is an example code:

    Pinput(
      controller: pinController,
    );

And set pinController's value in verificationCompleted callback:

    await FirebaseAuth.instance.verifyPhoneNumber(
      verificationCompleted: (PhoneAuthCredential credential) {
        pinController.setText(credential.smsCode);
      },
      verificationFailed: (FirebaseAuthException e) {},
      codeSent: (String verificationId, int? resendToken) {},
      codeAutoRetrievalTimeout: (String verificationId) {},
    );

If you aren't using firebase_auth, you have two options, SMS Retriever API and SMS User Consent API,

SmartAuth is a wrapper package for Flutter for these APIs, so go ahead and add it as a dependency.

SMS Retriever API

To use Retriever API you need the App signature - guide

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

Once you get the app signature, you should include it in the SMS message in you backend like so:

SMS example:

Your ExampleApp code is: 123456
kg+TZ3A5qzS

Example Code

Sms code will be automatically applied, without user interaction.

SMS User Consent API

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

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

FAQ

autofill isn't working on iOS?

  • Make sure you are using real device, not simulator
  • Temporary replace Pinput with TextField, and check if autofill works. If, not it's probably a problem with SMS you are getting, autofill doesn't work with most of the languages
  • If you are using non stable version of Flutter that might be cause because something might be broken inside the Framework

are you using firebase_auth?

You should set controller's value in verificationCompleted callback, here is an example code:

    Pinput(
      controller: pinController,
    );
    
    await FirebaseAuth.instance.verifyPhoneNumber(
      verificationCompleted: (PhoneAuthCredential credential) {
        pinController.setText(credential.smsCode);
      },
      verificationFailed: (FirebaseAuthException e) {},
      codeSent: (String verificationId, int? resendToken) {},
      codeAutoRetrievalTimeout: (String verificationId) {},
    );
Changelog

7.0.0 · 24/08/2026

  • BREAKING: migrated from package:flutter/material.dart to material_ui. Material was decoupled from the Flutter SDK; the in-SDK copy is frozen and scheduled for removal (flutter/flutter#184093, flutter/flutter#172942).
  • Fixes "No Material widget found" on Flutter 3.47 for apps using material_ui (#237) — debugCheckHasMaterial matches the ancestor by exact type, so a material_ui Scaffold never satisfied the legacy check.
  • BREAKING: requires Flutter >= 3.44.0 and Dart >= 3.12.0 (the floor material_ui declares). Apps still on package:flutter/material.dart should stay on 6.x.

6.0.2 · 4/01/2026

  • Added Prelude.so sponsorship badge

6.0.1 · 25/11/2025

  • fix: removed hintLocales PR

6.0.0 · 18/11/2025

5.0.2 · 01/09/2025

  • feat: add enableInteractiveSelection parameter flag - PR by bybabek

5.0.1 · 15/01/2025

  • fix: makes Semantics responsive to enabled flag - PR by sharabiddin

5.0.0 · 7/06/2024

  • Implemented Pinput.builder to build custom Pinput fields
  • Migrated deprecated imperative apply of Flutter's Gradle plugins example app
  • Removed smart_auth dependency which was responsible for SMS autofill (Not everyone might need it and it was causing some issues)
  • Migration guide

4.0.0 · 10/02/2024

  • Fixed RECEIVER_EXPORTED exception in android SDK 34 PR
  • Fix "Namespace not specified" error when upgrading to AGP 8.0 PR
  • Updated readme

3.0.1 · 25/08/2023

  • Fixed contextMenuBuilder

3.0.0 · 03/08/2023

  • Replaced separator and separatorPositions with separatorBuilder

    | Property | Meaning/Default | |--------------------------|:---------------:| | separatorBuilder | (_) => const SizedBox(width: 8)|

2.3.0 · 24/07/2023

  • Fixed AGP 4.2<= compatibility in smart_auth
  • Updated SDK constraints
  • Updated default SMS code matcher regex length to 8 digits

2.2.31 · 22/02/2023

Added:

Property Meaning/Default
enableIMEPersonalizedLearning false

Whether to enable that the IME update personalized data such as typing history and user dictionary data. This flag only affects Android. On iOS, there is no equivalent flag.

2.2.30 · 15/02/2023

  • Fixed "TapDownDetails not match the corresponding type" on Flutter's master channel - issue

2.2.30 · 15/02/2023

  • Fixed "TapDownDetails not match the corresponding type" on Flutter's master channel - issue

2.2.23 · 25/01/2023

  • Added correct SDK constraints
  • Improved readme

2.2.22 · 25/01/2023

  • Removed FocusTrapArea
  • Removed ToolbarOptions
  • Added contextMenuBuilder, which is replacement of ToolbarOptions
  • Added onTapOutside callback, which is invoked when a tap is detected outside of the Pinput
  • Fixed text selection behavior

2.2.21 · 29/12/2022

  • Fixed the case where app was crashing while reading the OTP

2.2.19 · 19/12/2022

  • Improved changelog
  • Added better demo screenshots

2.2.18 · 19/12/2022

2.2.17 · 19/12/2022

  • Added demo screenshot for pub
  • Added funding URLs

2.2.16 · 06/10/2022

  • Improved Docs

2.2.15 · 06/10/2022

  • Improved Docs

2.2.14 · 06/10/2022

  • Improved Example

2.2.13 · 03/10/2022

  • Bumped minimum Flutter SDK version to 2.0.0
  • Added
Property Meaning/Default
isCursorAnimationEnabled true

2.2.12 · 05/08/2022

  • Added
Property Meaning/Default
senderPhoneNumber null / Optional parameter for Android SMS User Consent API

2.2.11 · 14/06/2022

  • Fixed smsCodeMatcher (PR)

2.2.10 · 31/05/2022

  • When this widget receives focus and is not completely visible (for example scrolled partially off the screen or overlapped by the keyboard) then it will attempt to make itself visible by scrolling a surrounding [Scrollable], if one is present. This value controls how far from the edges of a [Scrollable] the TextField will be positioned after the scroll.
Property Meaning/Default
scrollPadding EdgeInsets.all(20)

2.2.9 · 16/05/2022

  • onCompleted mot called
  • Added tests

2.2.8 · 13/05/2022

  • Fixed dart 2.17 hints

2.2.7 · 04/04/2022

  • Option to listen for multiple sms on android
Property Meaning/Default
listenForMultipleSmsOnAndroid false

2.2.6 · 02/04/2022

  • Updated smart_auth
  • Updated readme

2.2.5 · 28/03/2022

Added

Property Meaning/Default
crossAxisAlignment CrossAxisAlignment.start

2.2.4 · 21/03/2022

  • Fixed onClipboardFound

2.2.3 · 19/03/2022

Added Android SMS Autofill support

Property Meaning/Default
androidSmsAutofillMethod Options to enable SMS autofill on Android
smsCodeMatcher Used to extract code from SMS for Android Autofill if [androidSmsAutofillMethod] is enabled

2.1.4 · 06/03/2022

Updated docs

Added

Property Meaning/Default
Validator To validate Pinput with or without Form
pinputAutovalidateMode PinputAutovalidateMode.onSubmit
errorBuilder To build custom error widget under the Pinput
errorTextStyle Standard error text style, displayed under the Pinput
toolbarEnabled If true, paste button will appear on longPress, doubleTap event / true
forceErrorState If true [errorPinTheme] will be applied and [errorText] will be displayed under the Pinput / false
errorText Text displayed under the Pinput if Pinput is invalid

2.0.4 · 03/03/2022

  • Updated docs
  • Added onLongPress

2.0.3 · 02/03/2022

  • Updated readme.

2.0.2 · 25/02/2022

Sorry guys this version will break your code 💙 Introduced PinTheme class to control state of the individual pin easily, see readme's Getting Started section for examples.

  • Refactored, renamed some properties.
  • Added new Pinput examples
  • With long press user can paste from clipboard
Changes
Old New
onSubmit onCompleted
fieldsCount length
obscureText obscuringCharacter
obscureText obscuringCharacter
eachFieldHeight PinTheme.height
eachFieldWidth PinTheme.width
eachFieldConstraints PinTheme.constraints
disabledDecoration PinTheme.disabledPinTheme
followingFieldDecoration PinTheme.followingPinTheme
selectedFieldDecoration PinTheme.focusedPinTheme
submittedFieldDecoration PinTheme.submittedPinTheme
eachFieldMargin PinTheme.margin
eachFieldPadding PinTheme.padding

1.2.1 · 10/09/2021

🔥🚀 Merged PRs and Fixed common issues

1.2.0 · 03/13/2021

🔥🚀 Now PinPut supports custom numpad.(See demos) Added checkClipboard property

1.1.0 · 03/02/2021

🔥🚀 Migrated to Null safety

1.0.0 · 01/14/2021

🔥🚀 Updated Example, Increased package version to 1.0.0 in order to make it more trustful

0.2.6 · 10/09/2020

🔥🚀 Added cursor, preFilledWidget, mainAxisSize and autovalidateMode properties.

0.2.5 · 08/30/2020

🔥🚀 Added fake cursor, separatorPositions, separator and optimized project with the help of community. credits to @furaiev, @Holofox ,

0.2.4 · 05/19/2020

🔥🚀 Fixed Focus problems. Updated readme.

0.2.3 · 04/12/2020

🔥🚀 Fixed Focus on click after back button click

0.2.2 · 04/09/2020

🔥🚀 Fixed Demo urls

0.2.1 · 04/09/2020

🔥🚀 Minor fixes and demos

0.2.0 · 04/07/2020

🔥🚀 Added some useful Documentation

0.2.0-dev.1 · 04/07/2020

🔥🚀 Breaking changes, changed widget building logic so now it supports:

  • Backspace on keyboard
  • Every pixel customization
  • Nice animations
  • Form validation
  • Ios auto fill · testing needed

0.1.10 -02/08/2019

👍 With the help of community: @xportation

  • Added Set autofocus on the first field when the attribute is defined|

0.1.9 · 07/02/2019

  • Added
Property Default/Meaning
onClear Clear button callback

0.1.8 · 06/14/2019

👍 With the help of community: @datvo0110, @almeynman

  • Fixed minor bugs
  • Added
Property Default/Meaning
containerHeight 100.0

0.1.7 · 05/12/2019

👍 With the help of community: @datvo0110, @mwgriffiths88, @inromualdo

  • Fixed minor bugs, check clipboard when app is resumed...
  • Added Properties ability to hide keyboard & custumize more
Property Default/Meaning
textCapitalization TextCapitalization.none

0.1.6 · 01/17/2019

  • Added Properties ability to hide keyboard & custumize more
Property Default/Meaning
clearButtonIcon Icon(Icons.backspace, size: 30)
pasteButtonIcon Icon(Icons.content_paste, size: 30)
unFocusWhen Default is False, True to hide keyboard
textStyle TextStyle(fontSize: 30)
spaceBetween space between fields Default: 10.0
inputDecoration Ability to style field's border, padding etc...

0.1.5 · 12/17/2018

  • Added Copy From Clipboard functionality if copied text length is equal to fields count
Property Default
pasteButtonIcon Icons.content_paste

*Note that

clearButtonEnabled will change with actionButtonEnabled in next release, right now if it is true both clear and paste functionality works

0.1.4 · 10/31/2018

  • Added
Property Default
autoFocus true

0.1.3+1 · 11026/2018

  • Minor fixes

0.1.3 · 10/26/2018

  • Transformed plugin to MVVM pattern
  • Fixed onSubmit call when all fields aren't filled
  • Updated Demo
  • Added
Property Default
clearButtonIcon Icons.backspace
clearButtonEnabled true
clearButtonColor 0xFF66BB6A

0.1.2 · 10/24/2018

  • Added
Property Default
borderRadius 5.0
keybaordType number
keyboardAction next

0.1.1 · 10/24/2018

  • Added
Property Default
onSubmit Function
fieldsCount 4
isTextObscure false
fontSize 40.0

0.0.1 · 10/24/2018

  • Initial release, working base functionality

Installing

export PUB_HOSTED_URL=https://pub.mirror.p-hatan.com
flutter pub add pinput
pubspec.yaml
name: pinput
version: 7.0.0
description: 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
homepage: https://github.com/Tkko/Flutter_PinPut
repository: https://github.com/Tkko/Flutter_PinPut
issue_tracker: https://github.com/Tkko/Flutter_Pinput/issues
topics:
  - otp
  - pin-input
  - pin-code
  - passcode
  - sms-autofill
screenshots:
  - description: 'Pin input example'
    path: example/pinput_demo_1.webp
  - description: 'Pin code example'
    path: example/pinput_demo_2.webp
funding:
  - https://ko-fi.com/flutterman

environment:
  sdk: '>=3.12.0 <4.0.0'
  flutter: ">=3.44.0"


dependencies:
  flutter:
    sdk: flutter
  cupertino_ui: ^1.0.0
  material_ui: ^1.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^6.0.0

Version history

VersionPublishedArchiveSHA-256
0.0.1On demandDownload44e44409…111cf7
0.1.1On demandDownload5ea7ef5a…fb55b1
0.1.2On demandDownloadc23c448d…fda295
0.1.3On demandDownload66b70a51…dcad5e
0.1.3+1On demandDownload946cbb3f…eb0577
0.1.4On demandDownloada62250b3…0efd21
0.1.5On demandDownloadecca65c8…26233a
0.1.6On demandDownload298593fc…6a2d3c
0.1.7On demandDownloaddf0d902a…9c22ab
0.1.8On demandDownload8e78d120…8587da
0.1.9On demandDownloada60e84c5…76c532
0.1.10On demandDownload1befd438…55f7f0
0.2.0-dev.1On demandDownloadd5cc451b…ba5239
0.2.0On demandDownload4df34961…2de755
0.2.1On demandDownloadd59e91e6…6cdf7a
0.2.2On demandDownload3eafeb0e…0f280e
0.2.3On demandDownload16aae22a…0eba31
0.2.4On demandDownload2da345a8…27195f
0.2.5On demandDownload35217f78…0ad8bb
0.2.6On demandDownloada6b5d5bd…8a54c0
1.0.0On demandDownload85b31e22…23880f
1.1.0On demandDownload14df87a3…b22973
1.2.0On demandDownload75210739…b48557
1.2.1On demandDownload9ec35fd1…a9a0aa
1.2.2On demandDownload27eb6904…392949
2.0.0On demandDownload838fd01a…05f17e
2.0.1On demandDownloadf7975379…79a840
2.0.2On demandDownload29489bdb…f666de
2.0.3On demandDownload8ee710ca…8f8d3e
2.0.4On demandDownload035ab6b6…f6e84d
2.1.0On demandDownload36e3682a…f9421a
2.1.1On demandDownloadab9f8eab…30ac62
2.1.2On demandDownload472f8558…93905a
2.1.3On demandDownloadb11bb5bf…9758f8
2.1.4On demandDownload89d8a3e0…73e44e
2.2.0On demandDownload25cf01fe…603255
2.2.1On demandDownloadccb2a82c…af7f2d
2.2.2On demandDownloade071a03d…c42f37
2.2.3On demandDownloada60837b8…a78806
2.2.4On demandDownload0aff884a…af925f
2.2.5On demandDownloadc92ed88d…b9830b
2.2.6On demandDownload749a120b…418e58
2.2.7On demandDownload3517b773…0de2ad
2.2.8On demandDownload78f8c7ff…06936f
2.2.9On demandDownload80aa8c39…05aa8e
2.2.10On demandDownload85ab9fc0…bc4e26
2.2.11On demandDownload55071b48…b7c788
2.2.12On demandDownload114112cd…476267
2.2.13On demandDownload40e4744b…75649c
2.2.14On demandDownloadbdc6d5df…708614
2.2.15On demandDownload8bcdbe10…b9a978
2.2.16On demandDownloadd49de461…202be2
2.2.17On demandDownload73e60816…517d4e
2.2.18On demandDownloadadac98bf…086531
2.2.19On demandDownloadbad02fee…5862ef
2.2.20On demandDownload2ad8afe7…8eb278
2.2.21On demandDownloade502557e…38869e
2.2.22On demandDownload45f65269…3be916
2.2.23On demandDownloade6aabd15…1e1f82
2.2.30On demandDownload121d3b16…368d4c
2.2.31On demandDownload1773743c…84bdcf
2.3.0On demandDownload543da5bf…5b813b
2.4.0On demandDownloadaa6b31d0…4244bf
3.0.0On demandDownload92b60664…c0a088
3.0.1On demandDownloada92b55ec…e4d805
4.0.0On demandDownload6d571e38…890efa
5.0.0On demandDownload7bf9aa7d…b048d0
5.0.1On demandDownload8a73be42…23d35a
5.0.2On demandDownloadc41f42ee…40aad2
6.0.0On demandDownloadbffcad45…5aae5d
6.0.1On demandDownload692e1c29…354819
6.0.2On demandDownload4c3f1b84…5d6e68
7.0.0Cached · Upstream SHA-256 matchedDownloadd446a2a0…a54fa4

Dependencies

cupertino_ui "^1.0.0"

flutter { "sdk": "flutter" }

material_ui "^1.0.0"