Untitled
unknown
plain_text
a year ago
9.1 kB
5
Indexable
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; typedef OnCodeEnteredCompletion = void Function(String value); typedef OnCodeChanged = void Function(String value); typedef HandleControllers = void Function(List<TextEditingController?> controllers); class OtpTextField extends StatefulWidget { final bool showCursor; final int numberOfFields; final double fieldWidth; final double borderWidth; final Color enabledBorderColor; final Color focusedBorderColor; final Color disabledBorderColor; final Color borderColor; final Color? cursorColor; final EdgeInsetsGeometry margin; final TextInputType keyboardType; final TextStyle? textStyle; final MainAxisAlignment mainAxisAlignment; final CrossAxisAlignment crossAxisAlignment; final OnCodeEnteredCompletion? onSubmit; final OnCodeEnteredCompletion? onCodeChanged; final HandleControllers? handleControllers; final bool obscureText; final bool showFieldAsBox; final bool enabled; final bool filled; final bool autoFocus; final bool readOnly; bool clearText; final bool hasCustomInputDecoration; final Color fillColor; final BorderRadius borderRadius; final InputDecoration? decoration; final List<TextStyle?> styles; final List<TextInputFormatter>? inputFormatters; OtpTextField({super.key, this.showCursor = true, this.numberOfFields = 4, this.fieldWidth = 40.0, this.margin = const EdgeInsets.only(right: 8.0), this.textStyle, this.clearText = false, this.styles = const [], this.keyboardType = TextInputType.number, this.borderWidth = 2.0, this.cursorColor, this.disabledBorderColor = const Color(0xFFE7E7E7), this.enabledBorderColor = const Color(0xFFE7E7E7), this.borderColor = const Color(0xFFE7E7E7), this.focusedBorderColor = const Color(0xFF4F44FF), this.mainAxisAlignment = MainAxisAlignment.center, this.crossAxisAlignment = CrossAxisAlignment.center, this.handleControllers, this.onSubmit, this.obscureText = false, this.showFieldAsBox = false, this.enabled = true, this.autoFocus = false, this.hasCustomInputDecoration = false, this.filled = false, this.fillColor = const Color(0xFFFFFFFF), this.readOnly = false, this.decoration, this.onCodeChanged, this.borderRadius = const BorderRadius.all(Radius.circular(4.0)), this.inputFormatters, }) : assert(numberOfFields > 0), assert(styles.isNotEmpty ? styles.length == numberOfFields : styles.isEmpty); @override _OtpTextFieldState createState() => _OtpTextFieldState(); } class _OtpTextFieldState extends State<OtpTextField> { late List<String?> _verificationCode; late List<FocusNode?> _focusNodes; late List<TextEditingController?> _textControllers; @override void initState() { super.initState(); _verificationCode = List<String?>.filled(widget.numberOfFields, null); _focusNodes = List<FocusNode?>.filled(widget.numberOfFields, null); _textControllers = List<TextEditingController?>.filled( widget.numberOfFields, null, ); } @override void didUpdateWidget(covariant OtpTextField oldWidget) { super.didUpdateWidget(oldWidget); if(oldWidget.clearText != widget.clearText && widget.clearText == true) { for (var controller in _textControllers ){ controller?.clear(); } _verificationCode = List<String?>.filled(widget.numberOfFields, null); setState((){ widget.clearText = false; }); } } @override void dispose() { super.dispose(); for (var controller in _textControllers) { controller?.dispose(); } } @override Widget build(BuildContext context) { return generateTextFields(context); } Widget _buildTextField({ required BuildContext context, required int index, TextStyle? style, }) { return Container( width: widget.fieldWidth, height: 40, margin: widget.margin, child: TextField( showCursor: widget.showCursor, keyboardType: widget.keyboardType, textAlign: TextAlign.center, maxLength: 1, readOnly: widget.readOnly, style: style ?? widget.textStyle, autofocus: widget.autoFocus, cursorColor: widget.cursorColor, controller: _textControllers[index], focusNode: _focusNodes[index], enabled: widget.enabled, inputFormatters: widget.inputFormatters, decoration: widget.hasCustomInputDecoration ? widget.decoration : InputDecoration( counterText: "", filled: widget.filled, fillColor: widget.fillColor, focusedBorder: widget.showFieldAsBox ? outlineBorder(widget.focusedBorderColor) : underlineInputBorder(widget.focusedBorderColor), enabledBorder: widget.showFieldAsBox ? outlineBorder(widget.enabledBorderColor) : underlineInputBorder(widget.enabledBorderColor), disabledBorder: widget.showFieldAsBox ? outlineBorder(widget.disabledBorderColor) : underlineInputBorder(widget.disabledBorderColor), border: widget.showFieldAsBox ? outlineBorder(widget.borderColor) : underlineInputBorder(widget.borderColor), ), obscureText: widget.obscureText, onChanged: (String value) { //save entered value in a list _verificationCode[index] = value; onCodeChanged(verificationCode: value); changeFocusToNextNodeWhenValueIsEntered( value: value, indexOfTextField: index, ); changeFocusToPreviousNodeWhenValueIsRemoved(value: value, indexOfTextField: index); onSubmit(verificationCode: _verificationCode); }, ), ); } OutlineInputBorder outlineBorder(Color color) { return OutlineInputBorder( borderSide: BorderSide( width: widget.borderWidth, color: color, ), borderRadius: widget.borderRadius, ); } UnderlineInputBorder underlineInputBorder(Color color) { return UnderlineInputBorder( borderSide: BorderSide( color: color, width: widget.borderWidth, ), ); } Widget generateTextFields(BuildContext context) { List<Widget> textFields = List.generate(widget.numberOfFields, (int i) { addFocusNodeToEachTextField(index: i); addTextEditingControllerToEachTextField(index: i); if (widget.styles.isNotEmpty) { return _buildTextField( context: context, index: i, style: widget.styles[i], ); } if (widget.handleControllers != null) { widget.handleControllers!(_textControllers); } return _buildTextField(context: context, index: i); }); return Row( mainAxisAlignment: widget.mainAxisAlignment, crossAxisAlignment: widget.crossAxisAlignment, children: textFields, ); } void addFocusNodeToEachTextField({required int index}) { if (_focusNodes[index] == null) { _focusNodes[index] = FocusNode(); } } void addTextEditingControllerToEachTextField({required int index}) { if (_textControllers[index] == null) { _textControllers[index] = TextEditingController(); } } void changeFocusToNextNodeWhenValueIsEntered({ required String value, required int indexOfTextField, }) { //only change focus to the next textField if the value entered has a length greater than one if (value.isNotEmpty) { //if the textField in focus is not the last textField, // change focus to the next textField if (indexOfTextField + 1 != widget.numberOfFields) { //change focus to the next textField FocusScope.of(context).requestFocus(_focusNodes[indexOfTextField + 1]); } else { //if the textField in focus is the last textField, unFocus after text changed _focusNodes[indexOfTextField]?.unfocus(); } } } void changeFocusToPreviousNodeWhenValueIsRemoved({ required String value, required int indexOfTextField, }) { //only change focus to the previous textField if the value entered has a length zero if (value.isEmpty) { //if the textField in focus is not the first textField, // change focus to the previous textField if (indexOfTextField != 0) { //change focus to the next textField FocusScope.of(context).requestFocus(_focusNodes[indexOfTextField - 1]); } } } void onSubmit({required List<String?> verificationCode}) { if (verificationCode.every((String? code) => code != null && code != '')) { if (widget.onSubmit != null) { widget.onSubmit!(verificationCode.join()); } } } void onCodeChanged({required String verificationCode}) { if (widget.onCodeChanged != null) { widget.onCodeChanged!(verificationCode); } } }
Editor is loading...
Leave a Comment