Flutter 实现6个验收码输入框
开箱即用,初始化时就唤起键盘,并选中第一个
import 'package:flutter/material.dart';import 'dart:async'; // 引入 Timer 类class VerificationCode extends StatefulWidget {final String phoneNumber;const VerificationCode({super.key, required this.phoneNumber});static const double horizontalPadding = 28.0;@overrideState<VerificationCode> createState() => _VerificationCode();
}class _VerificationCode extends State<VerificationCode> {// ... 你已有的变量Timer? _timer;int _start = 0; // 倒计时秒数(比如 60)bool _isCounting = false;// 倒计时逻辑void _startCountdown() {setState(() {_start = 60; // 60s 倒计时_isCounting = true;});_timer = Timer.periodic(const Duration(seconds: 1), (timer) {if (_start == 1) {timer.cancel();setState(() {_isCounting = false;});} else {setState(() {_start--;});}});}late TextEditingController _verificationController; // 验证码输入控制器late FocusNode _verificationFocusNode;String _verificationCode = '';@overridevoid initState() {super.initState();_verificationController = TextEditingController();_verificationFocusNode = FocusNode();// 监听验证码输入变化_verificationController.addListener(() {setState(() {_verificationCode = _verificationController.text;});if (_verificationCode.length == 6) {_forgetPasswordPage();}});}//忘记密码void _forgetPasswordPage() async {// 验证成功后跳转页面}@overridevoid dispose() {_timer?.cancel();_verificationController.dispose();_verificationFocusNode.dispose();super.dispose();}void _handleLogin() {// TODO: 实现登录逻辑}String _getPhoneNumberLastFourDigits() {try {if (widget.phoneNumber.isEmpty) return '已发送验证码';if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(widget.phoneNumber)) {return '已发送验证码';}final length = widget.phoneNumber.length;if (length >= 4) {return '已发送验证码至尾号${widget.phoneNumber.substring(length - 4)}';} else {return '已发送验证码';}} catch (_) {return '已发送验证码';}}@overrideWidget build(BuildContext context) {return Scaffold(resizeToAvoidBottomInset: true,body: Stack(children: [SingleChildScrollView(child: Container(height: MediaQuery.of(context).size.height,decoration: const BoxDecoration(color: Colors.white,image: DecorationImage(image: AssetImage('assets/pageBG/backgroundLogin.png'),fit: BoxFit.cover,),),child: Column(mainAxisAlignment: MainAxisAlignment.start,children: [const SizedBox(height: 56),Padding(padding: const EdgeInsets.only(left: 15),child: SizedBox(width: double.infinity,child: Column(crossAxisAlignment: CrossAxisAlignment.start,children: [GestureDetector(onTap: () {Navigator.pop(context);},child: Image.asset('assets/images/return.png',width: 20,height: 20,),),],),),),const SizedBox(height: 35),Container(margin: const EdgeInsets.symmetric(horizontal: VerificationCode.horizontalPadding,),alignment: Alignment.centerLeft,child: const Text('请输入验证码',style: TextStyle(color: Color.fromRGBO(51, 51, 51, 1),fontSize: 26,fontWeight: FontWeight.w500,),),),const SizedBox(height: 6),Container(width: double.infinity,margin: const EdgeInsets.symmetric(horizontal: VerificationCode.horizontalPadding,),child: Text(_getPhoneNumberLastFourDigits(),style: const TextStyle(color: Color.fromRGBO(102, 102, 102, 1),fontSize: 14,),),),const SizedBox(height: 42),// 验证码输入框GestureDetector(// 点击验证码输入框,使键盘弹出onTap: () {FocusScope.of(context,).requestFocus(_verificationFocusNode);},child: Container(width: double.infinity,height: 48,margin: const EdgeInsets.symmetric(horizontal: VerificationCode.horizontalPadding,),child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: List.generate(6, (index) {final isCurrentPosition =_verificationCode.length == index;final isFilled = _verificationCode.length > index;return Container(width: 45,height: 48,decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(8),border: Border.all(color:isCurrentPosition? const Color(0xFF4D7CFE) // 当前输入位置:高亮蓝色: const Color.fromRGBO(227,227,227,1,), // 默认灰色边框width: 1.5,),),alignment: Alignment.center,child: Text(isFilled ? _verificationCode[index] : '',style: const TextStyle(fontSize: 20,fontWeight: FontWeight.w500,color: Color(0xFF333333),),),);}),),),),// 隐藏输入框Offstage(offstage: true,child: TextField(controller: _verificationController,focusNode: _verificationFocusNode,keyboardType: TextInputType.number,maxLength: 6,autofocus: true,decoration: const InputDecoration(counterText: '', // 隐藏 maxLength 计数器border: InputBorder.none,),),),// 忘记密码Container(width: double.infinity,padding: EdgeInsets.only(right: _isCounting ? 20 : 28,top: 10,),child: GestureDetector(onTap:_isCounting? null: () {// 调用你发送验证码的接口_startCountdown();},child: Text(_isCounting ? '重新获取(${_start}s)' : '重新获取',style: TextStyle(color:_isCounting? Colors.grey: const Color(0xFF4D7CFE), // 蓝色fontSize: 14,),textAlign: TextAlign.right,),),),// 后续功能组件(如登录按钮)可继续添加],),),),],),);}
}