This code is working for laptop keyboard delete key press. But it’s not printing backspace pressed in iphone and android clear icon pressed . How to achieve this?
Enter the first three digits of the 4-digit PIN incorrectly.
Press the X button on the keyboard to clear the previous field.
Observe that the focus remains on the current field (PIN), even after pressing the X button multiple times, preventing the user from easily correcting the mobile number
Pressing the X button should move the focus to the previous field and clear it, allowing the user to re-enter the correct information.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class OTPPinWidget extends StatefulWidget {
final int length;
final bool isPassword;
final Function(String)? onComplete;
final double height;
final double width;
final double spaceBetween;
final double borderRadius;
final double borderWidth;
final Color borderColor;
final Color fillColor;
final MainAxisAlignment mainAxisAlignment;
final bool isError;
const OTPPinWidget({
super.key,
required this.length,
required this.isPassword,
this.onComplete,
this.height = 54,
this.width = 54,
this.spaceBetween = 16,
this.borderRadius = 8,
this.borderWidth = 1,
this.borderColor = Colors.black,
this.fillColor = Colors.transparent,
this.mainAxisAlignment = MainAxisAlignment.spaceBetween,
this.isError = false,
});
@override
_OTPPinWidgetState createState() => _OTPPinWidgetState();
}
class _OTPPinWidgetState extends State<OTPPinWidget> {
late List<TextEditingController> _controllers;
late List<FocusNode> _focusNodes;
final FocusNode _keyboardFocusNode = FocusNode();
@override
void initState() {
super.initState();
_controllers = List.generate(widget.length, (_) => TextEditingController());
_focusNodes = List.generate(widget.length, (_) => FocusNode());
}
@override
void dispose() {
for (var controller in _controllers) {
controller.dispose();
}
for (var focusNode in _focusNodes) {
focusNode.dispose();
}
_keyboardFocusNode.dispose();
super.dispose();
}
void clearTextFields() {
for (var controller in _controllers) {
controller.clear();
}
}
void handleBackspace() {
for (int i = widget.length - 1; i >= 0; i--) {
if (_focusNodes[i].hasFocus) {
if (_controllers[i].text.isEmpty && i > 0) {
_focusNodes[i - 1].requestFocus();
_controllers[i - 1].clear();
break;
}
}
}
}
@override
Widget build(BuildContext context) {
if (widget.isError) {
clearTextFields();
}
return Focus(
focusNode: _keyboardFocusNode,
onKeyEvent: (FocusNode node, KeyEvent event) {
if (event is KeyDownEvent) {
if (event.logicalKey == LogicalKeyboardKey.backspace ||
event.logicalKey == LogicalKeyboardKey.clear) {
print('Backspace pressed');
handleBackspace();
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
},
child: Row(
mainAxisAlignment: widget.mainAxisAlignment,
children: List.generate(widget.length, (index) {
return Container(
decoration: BoxDecoration(
border: Border.all(
width: widget.borderWidth, color: widget.borderColor),
borderRadius: BorderRadius.circular(widget.borderRadius),
),
child: SizedBox(
width: widget.width,
height: widget.height,
child: TextFormField(
controller: _controllers[index],
focusNode: _focusNodes[index],
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
obscureText: widget.isPassword,
obscuringCharacter: '●',
maxLength: 1,
textInputAction: index == widget.length - 1
? TextInputAction.done
: TextInputAction.next,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9]'))
],
onChanged: (value) {
if (value.isNotEmpty) {
if (index < widget.length - 1) {
FocusScope.of(context).nextFocus();
} else {
FocusScope.of(context).unfocus();
}
final otp = _controllers
.map((controller) => controller.text)
.join();
if (widget.onComplete != null) {
widget.onComplete!(otp);
}
}
},
onTap: () {
_controllers[index].selection = TextSelection.fromPosition(
TextPosition(offset: _controllers[index].text.length),
);
},
decoration: InputDecoration(
contentPadding: EdgeInsetsDirectional.only(
start: 4, top: widget.isPassword ? 36 : 24),
counter: const SizedBox.shrink(),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(widget.borderRadius),
borderSide:
const BorderSide(width: 0, color: Colors.transparent),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(widget.borderRadius),
borderSide:
const BorderSide(width: 0, color: Colors.transparent),
),
fillColor: widget.fillColor,
filled: true,
isDense: true,
counterText: '',
),
style: TextStyle(
color: const Color(0xFF221F1F),
fontSize: widget.isPassword ? 12 : 18,
fontWeight: FontWeight.w700,
),
),
),
);
}),
),
);
}
}