当前位置: 首页 > backend >正文

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,),),),// 后续功能组件(如登录按钮)可继续添加],),),),],),);}
}

http://www.xdnf.cn/news/9427.html

相关文章:

  • python多进程
  • 应用签名分发平台开发源码时数据储存是如何实现
  • vue3自定义指令来实现 v-focus 功能
  • LittleFS 小型文件系统(一)
  • HOW - 从0到1搭建自己的博客站点(三)
  • KV Cache:大模型推理加速的核心机制
  • shell脚本中的常用命令
  • AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年5月27日第90弹
  • 【系分】论文模版
  • w笔记--Swagger
  • 开源即战力!从科研到商用:Hello Robot 移动操作机器人Stretch 3多模态传感融合(RGB-D/激光/力矩)控制方案
  • 仿真环境中机器人抓取与操作 - 上手指南
  • java常用工具类:实现文件下载
  • AD-PCB--电子设计学习思路 DAY 1
  • 从零到一:影刀RPA学习者的破局之路
  • 分布式系统中的消息幂等性与流量控制(一)
  • Vue组件技术全解析大纲
  • mediapipe标注视频姿态关键点(基础版加进阶版)
  • Navicat 17 SQL 预览时表名异常右键表名,点击设计表->SQL预览->另存为的SQL预览时,表名都是 Untitled。
  • 【Elasticsearch】scripted_upsert
  • 小白成长之路-计算机网络(四)
  • BUG调试案例十二:LM5117输出电压纹波偏大问题案例
  • 初识 ProtoBuf
  • 破解Docker镜像下载难题
  • 永磁同步电机控制算法--变结构PI调节器
  • 面向测试编程——SmartRefreshLayout的测试case
  • Ubuntu系统开放 45876/tcp 端口
  • Cookie、Session和Token鉴权
  • Python实用脚本:可视化分割txt标签数据
  • TWTSolutions水厂污水厂设计计算软件:化学强化絮凝单元