CI codecov style license


The widget_toolkit_otp package contains a number of useful widgets that can help you with building sms/pin code screens or workflows for your app. The package contains widgets for entering pin codes, pasting them, resend logic and more.




The widget_toolkit_otp package consists of the following widgets:


SmsCodeField is a wrapper around package Pinput and exposes all its customization options. In addition it provides default implementations for some of the package properties and styles it in themeData. Supports auto fill and paste. By default it uses the inter feature communication, implemented with the SmsCodeBloc. To use it as stand alone widget, set its property useInternalCommunication to false.


CountdownWidget is used to keep track of the remaining time.

An onCountdownTick callback can be provided to keep track of any changes happening. This callback accepts the remaining countdown time to which the user of the widget can react. This callback is triggered after the frame has been rendered in order to prevent any potential setStates happening and modifying the widget during its build phase.

You can also access the remaining time, as well as the elapsed time of the countdown via a controller. The controller also gives you the possibility to reset the countdown and start it from a custom time (provided in seconds).

By default, every countdown is presented in the format of remaining seconds, however, you can change this to include minutes and hours by changing the timeFormat parameter of the widget.

By default, the highest number (minutes in the minutes format, hours in the hours format) will be displayed with double digits (even if a single digit number is presented). In case you want to disable this behavior, you can set the preferDoubleDigitsForTime parameter to false.


AutomatedResendCodeButton maintains its own state. Once the button gets pressed, it goes through a few statuses (loading, codeSent, disabled, active, error). The button is enabled only in the active state. There are a lot of options to be customized such as providing icons, labels, colorStyles, textStyles etc. To prevent the user from spam pressing the button it gets disabled for long after the onPressed callback is executed. That way we are not going to sent too many codes and confuse the user. Uses the CountdownWidget under the hood.


ResendCodeButton might be maintained from your business logic, but by default it uses SmsCodeBloc. It builds a TextButton with great amount of customization options and automated styling.


SmsPhoneNumberField is a kind of builder which provides a phone number and a callback to update the saved phone number. It is handy for building (if needed) a featured field, wrapped in a SmsCodeWidget.


ResendButtonTimer wraps CountdownWidget with the business logic provided from SmsCodeBloc. It has a builder, which allows you to build a custom widget in place of the CountdownWidget, using the same business logic.


ValidityWidget displays how long will be valid lately sent to the user code. This widget can not be used outside the SmsCodeBloc and its content might be fully recreated using its builder.


SmsCodeProvider is a user friendly way to provide all dependencies for the implemented business logic and, in the same step, arrange the page content depending on the bloc's states and events.


Step 1. Add the widget_toolkit_otp and widget_toolkit packages as dependencies:

$ flutter pub add widget_toolkit widget_toolkit_otp

Step 2. Import the packages by including them in you source file:

import 'package:widget_toolkit/widget_toolkit.dart';
import 'package:widget_toolkit_otp/widget_toolkit_otp.dart';

Step 3. Set the appropriate ThemeData:

return MaterialApp(  
  theme: ThemeData.light().copyWith(  
    colorScheme: ColorScheme.fromSwatch(),  
    extensions: [  
  darkTheme: ThemeData.dark().copyWith(  
    colorScheme: ColorScheme.fromSwatch(),  
    extensions: [  

How To Use

There are two ways of using this package - to use prebuilt widgets to visualize your own business logic, or to use the provided SmsCodeBloc and arrange widgets in a page however you want.

You can use the following widgets without SmsCodeBloc:

  • CountdownWidget
  • AutomatedResendCodeButton
  • SmsCodeField(set useInternalCommunication = false)
  • ResendCodeButton(set useInternalCommunication = false)

To use the implemented business logic, you first need to provide in the context SmsCodeBloc and an implementation of SmsCodeService. Then, to render your page content, you can use SmsPhoneNumberField (combine it with custom widget or with TextFieldDialog), ValidityWidget, ResendCodeButton, ResendButtonTimer and SmsCodeField. Use SmsCodeProvider to have all dependencies provided and use its builder to easily create your page content.

An example of how to achieve the integrated SmsCodeProvider functionality:

Step 1. Create an implementation of SmsCodeService:

class MyCustomSmsCodeService implements SmsCodeService {  
  Future<dynamic> confirmPhoneCode(String code) async {  
    //send a request to the server to verify the code and return the response...  
  Future<String> getFullPhoneNumber() async {  
    //fetch user's phone number...  
  Future<String> updatePhoneNumber(String newNumber) async {  
    //update the user's number and return new one...  
  Future<bool> sendConfirmationSms(String usersPhoneNumber) async {  
    //resend SMS code  
  Future<int> getValidityTime(bool reset) async {  
    //how long the sent code will be valid...  
  Future<int> getResendButtonThrottleTime(bool reset) async {  
    //time until next resend is available in seconds...  
  Future<int> getCodeLength() async {  
    //fetch code length...  

Step 2. Assemble SmsCodeProvider and its subsequent widgets:

  sentNewCodeActivationTime: 2,  
  smsCodeService: MyCustomSmsCodeService(),  
  builder: (state) => Column(  
    children: [  
        builder: (context, number, onChanged) => TextFieldDialog(  
            value: number,  
            onChanged: onChanged,  
      const SmsCodeField(),  
      const ValidityWidget(),  
      const ResendCodeButton(),  
      const ResendButtonTimer(),