Last active
February 27, 2024 20:24
-
-
Save marcusedu/bf8b2af7e54cd75a4d71a52d3adecd68 to your computer and use it in GitHub Desktop.
A simple MFA Validator Input, with custom length code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
), | |
), | |
], | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Preview