Skip to content

Instantly share code, notes, and snippets.

@marcusedu
Last active February 27, 2024 20:24
Show Gist options
  • Save marcusedu/bf8b2af7e54cd75a4d71a52d3adecd68 to your computer and use it in GitHub Desktop.
Save marcusedu/bf8b2af7e54cd75a4d71a52d3adecd68 to your computer and use it in GitHub Desktop.
A simple MFA Validator Input, with custom length code.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class MfaValidatorInput extends StatefulWidget {
const MfaValidatorInput({
super.key,
required this.length,
this.onCompleted,
this.onChanged,
this.focusNode,
this.keyboardType = TextInputType.number,
});
final int length;
final void Function(String)? onCompleted;
final void Function(String)? onChanged;
final FocusNode? focusNode;
final TextInputType keyboardType;
@override
State<MfaValidatorInput> createState() => _MfaValidatorInputState();
}
class _MfaValidatorInputState extends State<MfaValidatorInput> {
late final FocusNode _focusNode;
late final List<String?> _values;
late final List<FocusNode> _focusNodes;
late final List<TextEditingController> _controllers;
@override
void initState() {
_focusNode = widget.focusNode ?? FocusNode();
_values = List.filled(widget.length, null);
_focusNodes = List.generate(widget.length, (index) => FocusNode());
_controllers = List.generate(widget.length, (index) => TextEditingController());
super.initState();
}
FocusNode? getFocusedNode() {
for (var i = 0; i < widget.length; i++) {
if (_focusNodes[i].hasFocus) {
return _focusNodes[i];
}
}
return null;
}
String? getValueOnFocusedNode() {
for (var i = 0; i < widget.length; i++) {
if (_focusNodes[i].hasFocus) {
return _values[i];
}
}
return null;
}
@override
Widget build(BuildContext context) {
return KeyboardListener(
focusNode: _focusNode,
onKeyEvent: (event) {
if (event.logicalKey == LogicalKeyboardKey.backspace && event is KeyDownEvent) {
for (var i = widget.length - 1; i >= 0; i--) {
if (_focusNodes[i].hasFocus) {
if (_values[i] != null && _values[i]!.isNotEmpty) {
_controllers[i].clear();
_values[i] = null;
widget.onChanged?.call(_values.join());
} else {
if (i > 0) {
_focusNodes[i].unfocus();
_focusNodes[i - 1].requestFocus();
}
}
break;
}
}
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
for (var i = 0; i < widget.length; i++)
SizedBox.square(
dimension: 44,
child: TextFormField(
textAlign: TextAlign.center,
controller: _controllers[i],
keyboardType: widget.keyboardType,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
buildCounter: (context, {required currentLength, required isFocused, maxLength}) => null,
onChanged: (value) {
if (value.isEmpty) return;
_values[i] = null;
_values[i] = value;
if (i == widget.length - 1) {
_focusNodes[i].unfocus();
} else {
_focusNodes[i + 1].requestFocus();
}
if (_values.every((element) => element != null && element.isNotEmpty)) {
widget.onCompleted?.call(_values.join());
}
},
focusNode: _focusNodes[i],
maxLength: 1,
),
),
],
),
);
}
}
@marcusedu
Copy link
Author

Preview
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment