Please check this.
class OTPField extends StatefulWidget {
final OTPController otpController;
const OTPField({super.key, required this.otpController});
@override
State<OTPField> createState() => _OTPFieldState();
}
class _OTPFieldState extends State<OTPField> {
late List<TextEditingController> digitControllers;
late List<FocusNode> focusNodes;
@override
void initState() {
super.initState();
digitControllers = List.generate(4, (index) => TextEditingController());
focusNodes = List.generate(4, (index) => FocusNode());
}
@override
void dispose() {
for (var controller in digitControllers) {
controller.dispose();
}
for (var node in focusNodes) {
node.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(
4,
(index) => SizedBox(
width: 50,
height: 50,
child: KeyboardListener(
focusNode: FocusNode(),
onKeyEvent: (KeyEvent event) {
if (event is KeyDownEvent) {
if (event.logicalKey == LogicalKeyboardKey.backspace && digitControllers[index].text.isEmpty && index > 0) {
focusNodes[index - 1].requestFocus();
digitControllers[index - 1].clear();
}
}
return;
},
child: TextField(
controller: digitControllers[index],
focusNode: focusNodes[index],
keyboardType: TextInputType.number,
maxLength: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(
counterText: '',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
onChanged: (value) {
if (value.isNotEmpty && index < 3) {
focusNodes[index + 1].requestFocus();
}
String fullOtp = digitControllers.map((c) => c.text).join();
},
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
),
),
),
),
);
}
}
For me this worked it moves to next input on entering first digit
Row(
children: <Widget>[
Expanded(
child: TextFormField(
textInputAction: TextInputAction.next,
onChanged: (_) => FocusScope.of(context).nextFocus(),
controller:c1 ,)
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
textInputAction: TextInputAction.next,
onChanged: (_) => FocusScope.of(context).nextFocus(),
controller:c2 ,),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
controller:c3 ,
textInputAction: TextInputAction.next,
onChanged: (_) => FocusScope.of(context).nextFocus(),),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
controller:c4 ,
textInputAction: TextInputAction.next,
onChanged: (_) => FocusScope.of(context).nextFocus(),),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
controller:c5 ,
textInputAction: TextInputAction.next,
onChanged: (_) => FocusScope.of(context).nextFocus(),),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
controller:c6 ,
textInputAction: TextInputAction.next,
onChanged: (_) => FocusScope.of(context).unfocus(),
),
)
],
)