I want to format pin entry with a dash after at least 5 characters have been entered. So, each user enter the 10 digit pin code a dash is added automatically after every 5 characters.
TextField(
maxLength: 10,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.characters,
onChanged: (text) {
pin = text;
},
textAlign: TextAlign.left,
// keyboardType: TextInputType.visiblePassword,
decoration: InputDecoration(
errorText: _errorText,
icon: Icon(
Icons.dialpad,
),
labelText: '8-digit PIN',
contentPadding: EdgeInsets.symmetric(vertical: 10.0),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.transparent, width: 2),
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 2),
),
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xFFF696969), width: 1),
),
),
),
Solution 1: HTMHell
You should add a controller to your TextField
, so you can control the value of the text field.
TextField(
...
controller: _controller,
...
)
Change your onChange
event function to something like this:
onChanged: (String text) {
final sanitizedText = text.replaceAll('-', '');
_controller.text = _addDashes(sanitizedText);
_controller.selection = TextSelection.collapsed(offset: _controller.text.length);
},
This takes the current text inside the TextField
, removes the dashes, and stores it in a variable. Then the function replaces the text of the TextField
using the controller, to the value of the sanitized text with _addDashes
function applied to it. Additionally, it will move the cursor to the end of the text field, since it jumped to the beginning when we changed the value.
String _addDashes(String text) {
const int addDashEvery = 5;
String out = '';
for (int i = 0; i < text.length; i++) {
if (i + 1 > addDashEvery && i % addDashEvery == 0) {
out += '-';
}
out += text[i];
}
return out;
}
Solution 2: Wilson Toribio
You can try this approach, use TextEditingController to controller when the text reaches an specific length and _controller.selection to continue where the dash was added, if this selection property is not handled then the selection will be restarted to the beginning:
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
late FocusNode myFocusNode;
@override
void initState() {
myFocusNode = FocusNode();
_controller.addListener(() {
if(_controller.text.length == 4) {
_controller.text = _controller.text + '-';
_controller.selection = TextSelection.fromPosition(TextPosition(offset: _controller.text.length));;
}
});
super.initState();
}
@override
void didChangeDependencies() {
print(_controller.text);
super.didChangeDependencies();
}
@override
void dispose() {
_controller.dispose();
myFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlue,
body: Container(
child: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
maxLength: 10,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.characters,
controller: _controller,
focusNode: myFocusNode,
onChanged: (text) {},
textAlign: TextAlign.left,
decoration: const InputDecoration(
icon: Icon(
Icons.dialpad,
),
labelText: '8-digit PIN',
contentPadding: EdgeInsets.symmetric(vertical: 10.0),
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.transparent, width: 2),
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 2),
),
errorBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Color(0xFFF696969), width: 1),
),
),
),
],
),
),
),
),
);
}
}