验证码基本上是每个项目都需要用到的功能,这篇文章完成了一个验证码的输入框效果,并且拓展了一个输入上传车牌号(包括新能源),自定义键盘等功能。作者会根据自己的实现的思路来阐述一下功能的实现
验证码输入的实现
先看一下效果
实现思路是:
首先让键盘弹起来
使用TextField让键盘弹起来,并且通过设置TextField的属性,让用户感觉不到TextField的存在
TextField(
maxLength: 6,
onChanged: (value) {
},
controller: controller,
cursorWidth: 0,
cursorColor: Colors.transparent,
keyboardType: TextInputType.number,
style: TextStyle(color: Colors.transparent),
decoration: InputDecoration(
border: InputBorder.none,
counterText: '',
),
),
复制代码
这样就完全看不到TextField 的存在并且点击可以弹起键盘
展示输入的验证码
使用stack布局,在最低层构建一排container设置边框,然后将TextField放在其上面,这样就会可以了,再监听onChanged方法,进行遍历TextField的输入值,显示在一排排的container上面。
Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: dataList
.map<Widget>((e) => renderContainer(e))
.toList(),
),
TextField(
maxLength: 6,
onChanged: (value) {
List data = [];
for (int i = 0; i < value.length; i++) {
data.add(value.substring(i, i + 1));
}
data = fillData(data);
if (mounted) {
setState(() {
dataList = data;
});
}
},
controller: controller,
cursorWidth: 0,
cursorColor: Colors.transparent,
keyboardType: TextInputType.number,
style: TextStyle(color: Colors.transparent),
decoration: InputDecoration(
border: InputBorder.none,
counterText: '',
),
),
],
)
复制代码
基本代码完成了,下面是全部代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
class CarInputPage extends StatefulWidget {
CarInputPage({Key key}) : super(key: key);
@override
_CarInputPageState createState() => _CarInputPageState();
}
class _CarInputPageState extends State<CarInputPage> {
TextEditingController controller = TextEditingController();
List dataList = ['', '', '', '', '', ''];
renderContainer(title) {
return Container(
width: 60,
height: 80,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(
color: title == '' ? Colors.green : Colors.blue, width: 2)),
child: Text(
title,
style: TextStyle(fontSize: 18, color: Colors.blue),
),
);
}
List fillData(List data) {
if (data.length < 6) {
data.add('');
this.fillData(data);
}
return data;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: customAppbar(title: '绑定车辆'),
body: GestureDetector(
onTap: () {
print('object');
SystemChannels.textInput.invokeMethod('TextInput.hide');
},
child: Container(
width: Get.width,
decoration: BoxDecoration(color: Colors.white),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
child: Column(
children: [
Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: dataList
.map<Widget>((e) => renderContainer(e))
.toList(),
),
TextField(
maxLength: 6,
onChanged: (value) {
List data = [];
for (int i = 0; i < value.length; i++) {
data.add(value.substring(i, i + 1));
}
data = fillData(data);
if (mounted) {
setState(() {
dataList = data;
});
}
},
controller: controller,
cursorWidth: 0,
cursorColor: Colors.transparent,
keyboardType: TextInputType.number,
style: TextStyle(color: Colors.transparent),
decoration: InputDecoration(
border: InputBorder.none,
counterText: '',
),
),
],
)
],
),
),
),
);
}
}
复制代码
可以上面的效果
自定义车牌号键盘
其中第一部分是每个省份的简写,之后就是字母和数组的组合。 使用自定义弹出悬浮窗。这次就不需要使用TextField进行弹出操作,在stack布局上添加点击方法就可以了。(ps: 数据源有可能不准确)
Get.dialog(StatefulBuilder(
builder: (context1, setBottomSheetState) {
return UnconstrainedBox(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.only(
bottom: Get.context.mediaQueryPadding.bottom),
width: Get.width,
decoration: BoxDecoration(
color: Colors.white,
),
child: Wrap(
children: keybordData
.map<Widget>((e) =>
renderNumberOne(e, setBottomSheetState))
.toList(),
)),
);
}),
barrierColor: Colors.transparent,
useRootNavigator: false,
useSafeArea: false);
复制代码
自定义键盘与数据展示的联动
下面展示的是点击切换键盘效果,主要实现思路是,控制自定义键盘内的数据,通过type区分展示不同的数据。fillData(title) {
if (dataList.length > 7) {
return;
}
List temp = dataList;
int index = -1;
for (int i = 0; i < temp.length; i++) {
if (temp[i] == '' || temp[i] == '新') {
index = i;
break;
}
}
if (index != -1) {
temp[index] = title;
}
setState(() {
dataList = temp;
});
}
renderNumberOne(title, setBottomSheetState) {
return Container(
alignment: Alignment.center,
width: Get.width / 8,
height: Get.width / 8 * 1.2,
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.grey),
),
child: Material(
child: InkWell(
onTap: () {
if (type == 0) {
//选择省份
List temp = dataList;
temp[0] = title;
setBottomSheetState(() {
type = 1;
keybordData = wordList;
});
setState(() {
dataList = temp;
});
} else if (type == 1) {
if (title == '0~9') {
setBottomSheetState(() {
type = 2;
keybordData = numberList;
});
} else {
fillData(title);
}
} else if (type == 2) {
if (title == 'A~Z') {
setBottomSheetState(() {
type = 1;
keybordData = wordList;
});
} else {
fillData(title);
}
}
},
child: title == '删除'
? Icon(
Icons.delete,
size: 18,
)
: Text(
title,
style: TextStyle(fontSize: 16, color: Colors.black),
),
)),
);
}
复制代码
最后就是点击删除按钮的逻辑
deleData(title, setBottomSheetState) {
List temp = dataList;
int index = -1;
for (int i = 0; i < temp.length; i++) {
if (temp[i] != '' && temp[i] != '新') {
index = i;
}
}
if (index != -1) {
temp[index] = index == 6 ? '新' : '';
}
if (index == 0) {
setBottomSheetState(() {
type = 1;
keybordData = province;
});
}
setState(() {
dataList = temp;
});
}
复制代码
效果:
over~~~