在做一个项目的时候发现关于离线验证码的组件真的少,经过一番面向搜索引擎编程【百度】以后发现了一个非常小众,很少人使用的一个包hb_check_code: ^0.0.2( https://pub.flutter-io.cn/packages/hb_check_code);在此感谢这个大佬的实现,我便在大佬的肩上接着弄成自己项目需要的样子。
因为原本项目的验证码控件是使用原生Android的方式实现的,后面我对照了一下跟大佬的实现方法其实是差不多的,但是太久没有用上Flutter跟Dart都生疏了。
话不多说先上图片让大家看看:

现在我们来解析要想实现一直这个随机验证码图片要经历那些过程:
1、创建空文件快捷生成一个StatefulWidget组件类;
2、随机生成英文字符跟数字;
3、将这些随机生成的字符跟用来模糊数据的点与横线画出来。
不要打我,步骤就是这么简单,实现才是重点
首先实现我们先来生成一个简单组件类--就随便取个名字吧。【VerifyCode】
class VerifyCode extends StatefulWidget {
final int vCodeNum; //用来决定是生成多少位的验证码,默认4位
final double width; //设置控件宽度 默认70
final double height; //设置控件高度 默认25
final Color backgroundColor; //设置背景色
//回调---用于获取随机的数据
final ValueChanged<String> verifyCallback;
const VerifyCode({Key key, this.vCodeNum=4, this.width=70, this.height=25, this.backgroundColor, this.verifyCallback}) : super(key: key);
@override
_VerifyCodeState createState() => _VerifyCodeState();
}
class _VerifyCodeState extends State<VerifyCode> {
@override
Widget build(BuildContext context) {}
}
上面就是简单是实现了一个验证码组件该有的东西,接下来我们来实现随机生成英文字符跟数字的方法:
//先定义一个非常low的常量
const _charsAll = ['1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j',
'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z'];
//然后定义一个随机生成字符串的方法
String getRandomString(){
String code = "";
for (var i = 0; i < widget.vCodeNum; i++) {
code = code + _charsAll[Random().nextInt(_charsAll.length)];
}
return code;
}
是不是非常low,慢慢来,后面会把源码都完整贴上。让大家自己去做修改
接下来就是开始画图了,话不多说直接上代码:
//随机生成绘图数据
//要是觉得干扰不够,你可以多画点线或者多画点。直接把widget.vCodeNum 这个参数另外定义一个用来控制线或者点数量的值
Map getRandomData(String srt) {
// list
List list = srt.split("");
// X坐标
double x = 0.0;
// 最大字体大小
double maxFontSize = 25.0;
//将painter保存起来,先计算出位置
List mList = [];
for (String item in list) {
...
}
double offsetX = (widget.width - x) / 2;
List dotData = [];
List lineData = [];
//绘制干扰点
for (var i = 0; i < widget.vCodeNum; i++) {
...
}
//绘制干扰线
for (var i = 0; i < widget.vCodeNum; i++) {
...
}
Map checkCodeDrawData = {
"painterData": mList,
"offsetX": offsetX,
"dotData": dotData,
"lineData": lineData,
};
return checkCodeDrawData;
}
//数据绘制
class VerifyCodePainter extends CustomPainter {
final Map drawData;
VerifyCodePainter({
@required this.drawData,
});
//画笔定义
Paint _paint = new Paint()
..color = Colors.grey
..strokeCap = StrokeCap.square
..isAntiAlias = true
..strokeWidth = 1.0
..style = PaintingStyle.fill;
@override
void paint(Canvas canvas, Size size) {
List mList = drawData["painterData"];
double offsetX = drawData["offsetX"];
//为了能居中显示移动画布
canvas.translate(offsetX, 0);
//从Map中取出值,直接绘制
for (var item in mList) {
..
}
// //将画布平移回去
canvas.translate(-offsetX, 0);
List dotData = drawData["dotData"];
for (var item in dotData) {
//干扰点的绘制
...
}
List lineData = drawData["lineData"];
for (var item in lineData) {
//干扰线的绘制
...
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return this != oldDelegate;
}
}
这样整体的工作就完成的差不多了,现在就差把整个组件实现起来;剩下的我就把完整的代码附上把毕竟摸鱼的人有什么坏心眼呢?
文件名:verifycode_widget.dart 欢迎自取有需要的自己改吧! 再次还是感谢【尤先森】大佬。hb_check_code: ^0.0.2(https://pub.flutter-io.cn/packages/hb_check_code)
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_tyyjk/util/log_utils.dart';
const _charsAll = ['1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j',
'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z'];
class VerifyCode extends StatefulWidget {
final int vCodeNum;
final double width;
final double height;
final Color backgroundColor;
//回调
final ValueChanged<String> verifyCallback;
const VerifyCode({Key key, this.vCodeNum=4, this.width=70, this.height=25, this.backgroundColor, this.verifyCallback}) : super(key: key);
@override
_VerifyCodeState createState() => _VerifyCodeState();
}
class _VerifyCodeState extends State<VerifyCode> {
String _rdStr = '';
double maxWidth = 0.0;
Map _drawData;
String getRandomString(){
String code = "";
for (var i = 0; i < widget.vCodeNum; i++) {
code = code + _charsAll[Random().nextInt(_charsAll.length)];
}
return code;
}
//随机生成绘图数据
Map getRandomData(String srt) {
// list
List list = srt.split("");
// X坐标
double x = 0.0;
// 最大字体大小
double maxFontSize = 25.0;
//将painter保存起来,先计算出位置
List mList = [];
for (String item in list) {
Color color = Color.fromARGB(255, Random().nextInt(255),
Random().nextInt(255), Random().nextInt(255));
int fontWeight = Random().nextInt(9);
TextSpan span = TextSpan(
text: item,
style: TextStyle(
color: color,
fontWeight: FontWeight.values[fontWeight],
fontSize: maxFontSize - Random().nextInt(10)));
TextPainter painter =
TextPainter(text: span, textDirection: TextDirection.ltr);
painter.layout();
double y =
Random().nextInt(widget.height.toInt()).toDouble() - painter.height;
if (y < 0) {
y = 0;
}
Map strMap = {"painter": painter, "x": x, "y": y};
mList.add(strMap);
x += painter.width + 3;
}
double offsetX = (widget.width - x) / 2;
List dotData = [];
List lineData = [];
//绘制干扰点
for (var i = 0; i < widget.vCodeNum; i++) {
int r = Random().nextInt(255);
int g = Random().nextInt(255);
int b = Random().nextInt(255);
double x = Random().nextInt(widget.width.toInt() - 5).toDouble();
double y = Random().nextInt(widget.height.toInt() - 5).toDouble();
double dotWidth = Random().nextInt(6).toDouble();
Color color = Color.fromARGB(255, r, g, b);
Map dot = {"x": x, "y": y, "dotWidth": dotWidth, "color": color};
dotData.add(dot);
}
//绘制干扰线
for (var i = 0; i < widget.vCodeNum; i++) {
int r = Random().nextInt(255);
int g = Random().nextInt(255);
int b = Random().nextInt(255);
double x = Random().nextInt(widget.width.toInt() - 5).toDouble();
double y = Random().nextInt(widget.height.toInt() - 5).toDouble();
double lineLength = Random().nextInt(20).toDouble();
Color color = Color.fromARGB(255, r, g, b);
Map line = {"x": x, "y": y, "lineLength": lineLength, "color": color};
lineData.add(line);
}
Map checkCodeDrawData = {
"painterData": mList,
"offsetX": offsetX,
"dotData": dotData,
"lineData": lineData,
};
return checkCodeDrawData;
}
@override
void initState() {
// TODO: implement initState
_rdStr = getRandomString();
LogUtil.e(_rdStr);
_drawData = getRandomData(_rdStr);
//计算最大宽度做自适应
maxWidth = getTextSize("8" * _rdStr.length,
TextStyle(fontWeight: FontWeight.values[8], fontSize: 25))
.width;
//数据回调
widget.verifyCallback(_rdStr);
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
color: widget.backgroundColor,
width: maxWidth > widget.width ? maxWidth : widget.width,
height: widget.height,
child: InkWell(
onTap: (){
setState(() {
_rdStr = getRandomString();
_drawData = getRandomData(_rdStr);
//数据回调
widget.verifyCallback(_rdStr);
});
},
child: CustomPaint(
painter: VerifyCodePainter(drawData: _drawData),
),
));
}
Size getTextSize(String text, TextStyle style) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
maxLines: 1,
textDirection: TextDirection.ltr)
..layout(minWidth: 0, maxWidth: double.infinity);
return textPainter.size;
}
}
class VerifyCodePainter extends CustomPainter {
final Map drawData;
VerifyCodePainter({
@required this.drawData,
});
Paint _paint = new Paint()
..color = Colors.grey
..strokeCap = StrokeCap.square
..isAntiAlias = true
..strokeWidth = 1.0
..style = PaintingStyle.fill;
@override
void paint(Canvas canvas, Size size) {
List mList = drawData["painterData"];
double offsetX = drawData["offsetX"];
//居中显示移动画布
canvas.translate(offsetX, 0);
//从Map中取出值,直接绘制
for (var item in mList) {
TextPainter painter = item["painter"];
double x = item["x"];
double y = item["y"];
painter.paint(
canvas,
Offset(x, y),
);
}
// //将画布平移回去
canvas.translate(-offsetX, 0);
List dotData = drawData["dotData"];
for (var item in dotData) {
double x = item["x"];
double y = item["y"];
double dotWidth = item["dotWidth"];
Color color = item["color"];
_paint.color = color;
canvas.drawOval(Rect.fromLTWH(x, y, dotWidth, dotWidth), _paint);
}
List lineData = drawData["lineData"];
for (var item in lineData) {
double x = item["x"];
double y = item["y"];
double lineLength = item["lineLength"];
Color color = item["color"];
_paint.color = color;
canvas.drawLine(Offset(x,y),Offset(x+lineLength,y), _paint);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return this != oldDelegate;
}
}
最后
为了让大家更好的去学习和提升自己,我和几位大佬一起收录整理的 Flutter进阶资料以及Android学习PDF+架构视频+面试文档+源码笔记 ,并且还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料……
这些都是我闲暇时还会反复翻阅的精品资料。可以有效的帮助大家掌握知识、理解原理。当然你也可以拿去查漏补缺,提升自身的竞争力。
如果你有需要的话,可以前往 GitHub 自行查阅。

本文介绍了如何在Flutter中实现一个离线验证码组件,通过创建StatefulWidget,随机生成英文字符和数字,然后绘制到图像上。详细步骤包括组件类的创建、随机字符生成以及画图代码展示。提供了完整源码和学习资源链接。
1801

被折叠的 条评论
为什么被折叠?



