首頁>技術>

在做一個專案的時候發現關於離線驗證碼的元件真的少,經過一番面向搜尋引擎程式設計【百度】以後發現了一個非常小眾,很少人使用的一個包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; }}

這樣整體的工作就完成的差不多了,現在就差把整個元件實現起來;剩下的我就把完整的程式碼附上把畢竟摸魚的人有什麼壞心眼呢?

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;  }}
最後

在這裡,我也準備了和大佬的交流筆記,還有一些大佬推薦的學習資料以及面試資料,除開大佬推薦的,我自己也準備了一些底層原理解析,專案實戰,面試專題。需要的可以私信我【資料】免費獲得。

底層原理+專案實戰+面試專題

1、底層原理(Android進階、架構設計、NDK、跨平臺、底層原始碼....)

2、專案實戰(一線網際網路大廠真實專案實戰訓練)

3、面試專題(大廠內推+簡歷最佳化+面試技巧+Android築基+分散式+開源框架+微服務架構+效能最佳化+JVM 等一線網際網路企業面試題)

資料對標對標阿里 P7,覆蓋 99% 網際網路公司技術要求

6
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • linux必學的50個命令