Flutter实现验证码输入框
思路:使用Stack布局,下层设计出每个框的样式,上层使用一个透明的输入框。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:get/get.dart';
class CodePage extends StatefulWidget {
final String tel;
CodePage(this.tel);
@override
_CodePageState createState() => _CodePageState();
}
class _CodePageState extends State<CodePage> {
//重新发送验证码计时器
Timer _codeTimer;
//重新发送验证码倒计时
int _codeTime = 60;
//是否重新发送验证码
bool _reSendCode = false;
//textfield焦点
FocusNode _codeFocus = new FocusNode();
//当前输入验证码的位置
int _currentCount = 0;
//闪烁光标计时器
Timer _timer;
//验证码
String _code = "";
//闪烁光标颜色
Color _cursorColor = Colors.transparent;
//textfield控制器
TextEditingController _codeController = new TextEditingController();
@override
void initState() {
// TODO: implement initState
super.initState();
//进入页面自动获取焦点,弹出键盘
_codeFocus.requestFocus();
//闪烁光标
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
_cursorColor = Colors.transparent;
if (mounted) {
setState(() {});
}
Future.delayed(Duration(milliseconds: 500), () {
_cursorColor = Colors.blue;
if (mounted) {
setState(() {});
}
});
});
}
@override
void dispose() {
// TODO: implement dispose
//判断计时器是否活跃,活跃取消
if (_timer.isActive) {
_timer.cancel();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: ScreenUtil().setSp(16),
),
onPressed: () {
Get.back();
},
),
),
body: SingleChildScrollView(
child: Container(
color: Colors.white,
width: ScreenUtil().screenWidth,
height: ScreenUtil().screenHeight,
padding: EdgeInsets.only(
left: ScreenUtil().setWidth(20),
right: ScreenUtil().setWidth(20),
top: ScreenUtil().setHeight(20),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: ScreenUtil().setHeight(34),
margin: EdgeInsets.only(bottom: ScreenUtil().setHeight(8)),
child: Text(
"请输入6位验证码",
style: TextStyle(
fontSize: ScreenUtil().setSp(28),
),
),
),
Container(
height: ScreenUtil().setHeight(22),
margin: EdgeInsets.only(bottom: ScreenUtil().setHeight(56)),
child: Text(
"验证码已发送至+86 ${widget.tel}",
style: TextStyle(
fontSize: ScreenUtil().setSp(16),
color: Color.fromRGBO(127, 127, 127, 1),
),
),
),
Container(
height: ScreenUtil().setHeight(20),
margin: EdgeInsets.only(bottom: ScreenUtil().setHeight(10)),
child: Text(
"6位验证码",
style: TextStyle(
fontSize: ScreenUtil().setSp(14),
),
),
),
Container(
width: ScreenUtil().setWidth(368),
height: ScreenUtil().setHeight(48),
margin: EdgeInsets.only(bottom: ScreenUtil().setHeight(59)),
child: Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
buildCodeCell(0),
SizedBox(
width: ScreenUtil().setWidth(8),
),
buildCodeCell(1),
SizedBox(
width: ScreenUtil().setWidth(8),
),
buildCodeCell(2),
SizedBox(
width: ScreenUtil().setWidth(8),
),
buildCodeCell(3),
SizedBox(
width: ScreenUtil().setWidth(8),
),
buildCodeCell(4),
SizedBox(
width: ScreenUtil().setWidth(8),
),
buildCodeCell(5),
],
),
Opacity(
opacity: 0,
child: SizedBox(
width: ScreenUtil().screenWidth,
child: TextField(
onChanged: (value) {
setState(() {
_code = value;
_currentCount = value.length;
});
if (value.length == 6) {
checkCode();
}
},
focusNode: _codeFocus,
maxLength: 6,
controller: _codeController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
counterText: "",
),
textAlign: TextAlign.center,
style: TextStyle(fontSize: ScreenUtil().setSp(20)),
),
),
)
],
),
),
Container(
width: ScreenUtil().setWidth(335),
height: ScreenUtil().setHeight(42),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromRGBO(0, 138, 133, 1),
),
),
onPressed: () {},
child: Text(
"登录",
style: TextStyle(
fontSize: ScreenUtil().setSp(14), color: Colors.white),
),
),
),
Container(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
!_reSendCode
? TextButton(
child: Text("没收到验证码?"),
onPressed: () {
showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text("提示"),
content: Text("是否重新发送验证码"),
actions: [
CupertinoDialogAction(
child: Text("确认"),
onPressed: () {
Fluttertoast.showToast(
msg: "验证码已重新发送,请注意短信提示");
Get.back();
//退出选择框后设置重新发送验证码
_reSendCode = true;
//设置验证码时间60
_codeTime = 60;
//设置计时器,每秒减1
_codeTimer = Timer.periodic(
Duration(seconds: 1), (timer) {
//如果验证码时间为0
if (_codeTime == 0) {
_reSendCode = false;
setState(() {});
return;
} else {
//不为0没秒减1
_codeTime--;
setState(() {});
}
});
},
),
CupertinoDialogAction(
child: Text("取消"),
onPressed: () {
Get.back();
},
),
],
);
});
},
)
: Container(
margin: EdgeInsets.only(top: ScreenUtil().setHeight(8)),
child: Text("重新发送验证码($_codeTime)")),
],
),
),
],
),
),
),
);
}
GestureDetector buildCodeCell(int pos) {
return GestureDetector(
onTap: () {
_codeFocus.requestFocus();
},
child: Container(
width: ScreenUtil().setWidth(48),
height: ScreenUtil().setHeight(48),
decoration: BoxDecoration(
border: Border(
bottom: _currentCount != pos
? BorderSide(color: Colors.black, width: 1)
: BorderSide(width: 0, color: Colors.transparent),
),
),
child: _currentCount == pos
? Center(
child: Text(
"|",
style: TextStyle(fontSize: 25, color: _cursorColor),
),
)
: _code.length > pos
? Center(
child: Text(
"${_code[pos]}",
style: TextStyle(fontSize: 25),
))
: null,
),
);
}
void checkCode() {
print(_code);
}
}
|