美文网首页flutter
Flutter-自定义短信验证码

Flutter-自定义短信验证码

作者: 一笑轮回吧 | 来源:发表于2022-09-14 20:07 被阅读0次

    效果图(Flutter版本)

    简介

    前几天我发布了一个Android版本的短信验证码,今天发布Flutter版本,其实实现思路和原生版本是一模一样,可以说是直接把原生的绘制代码复制粘贴到Flutter项目中,kt修改为dart,实现样式还是下面四种:

    • 表格类型
    • 方块类型
    • 横线类型
    • 圈圈类型

    所以这里就不在阐述实现思路了,你也可以直接查看Android版本,点击
    Android-自定义短信验证码

    这里直接上全部代码,一把梭~


    代码

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    /*
     * 模式
     */
    enum CodeMode {
      //文字
      text
    }
    
    /*
     * 样式
     */
    enum CodeStyle {
      //表格
      form,
      //方块
      rectangle,
      //横线
      line,
      //圈圈
      circle
    }
    
    /*
     * 验证码
     */
    class CodeWidget extends StatefulWidget {
      CodeWidget({
        Key? key,
        this.maxLength = 4,
        this.height = 50,
        this.mode = CodeMode.text,
        this.style = CodeStyle.form,
        this.codeBgColor = Colors.transparent,
        this.borderWidth = 2,
        this.borderColor = Colors.grey,
        this.borderSelectColor = Colors.red,
        this.borderRadius = 3,
        this.contentColor = Colors.black,
        this.contentSize = 16,
        this.itemWidth = 50,
        this.itemSpace = 16,
      }) : super(key: key) {
        //如果是表格样式,就不设置Item之间的距离
        if (style == CodeStyle.form) {
          itemSpace = 0;
        }
      }
    
      late int maxLength;
    
      //高度
      late double height;
    
      //验证码模式
      late CodeMode mode;
    
      //验证码样式
      late CodeStyle style;
    
      //背景色
      late Color codeBgColor;
    
      //边框宽度
      late double borderWidth;
    
      //边框默认颜色
      late Color borderColor;
    
      //边框选中颜色
      late Color borderSelectColor;
    
      //边框圆角
      late double borderRadius;
    
      //内容颜色
      late Color contentColor;
    
      //内容大小
      late double contentSize;
    
      // 单个Item宽度
      late double itemWidth;
    
      //Item之间的间隙
      late int itemSpace;
    
      @override
      State<CodeWidget> createState() => _CodeWidgetState();
    }
    
    class _CodeWidgetState extends State<CodeWidget> {
      FocusNode focusNode = FocusNode();
    
      TextEditingController controller = TextEditingController();
    
      @override
      Widget build(BuildContext context) {
        return SizedBox(
          height: widget.height,
          child: LayoutBuilder(
            builder: (BuildContext context, BoxConstraints constraints) {
              var size = Size(constraints.maxWidth, constraints.maxHeight);
              return CustomPaint(
                size: size,
                painter: CodeCustomPainter(
                  widget.maxLength,
                  size.width,
                  size.height,
                  widget.mode,
                  widget.style,
                  widget.codeBgColor,
                  widget.borderWidth,
                  widget.borderColor,
                  widget.borderSelectColor,
                  widget.borderRadius,
                  widget.contentColor,
                  widget.contentSize,
                  widget.itemWidth,
                  widget.itemSpace,
                  focusNode,
                  controller,
                ),
                child: TextField(
                  //控制焦点
                  focusNode: focusNode,
                  controller: controller,
                  //光标不显示
                  showCursor: false,
                  //光标颜色透明
                  cursorColor: Colors.transparent,
                  enableInteractiveSelection: false,
                  //设置最大长度
                  maxLength: widget.maxLength,
                  //文字样式为透明
                  style: const TextStyle(
                    color: Colors.transparent,
                  ),
                  //只允许数据数字
                  inputFormatters: [
                    FilteringTextInputFormatter.digitsOnly,
                  ],
                  //弹出数字键盘
                  keyboardType: TextInputType.number,
                  //边框样式取消
                  decoration: null,
                ),
              );
            },
          ),
        );
      }
    }
    
    class CodeCustomPainter extends CustomPainter {
      double width;
      double height;
      int maxLength;
    
      //验证码模式
      late CodeMode mode;
    
      //验证码样式
      late CodeStyle style;
    
      //背景色
      Color codeBgColor;
    
      //边框宽度
      double borderWidth;
    
      //边框默认颜色
      Color borderColor;
    
      //边框选中颜色
      Color borderSelectColor;
    
      //边框圆角
      double borderRadius;
    
      //内容颜色
      Color contentColor;
    
      //内容大小
      double contentSize;
    
      // 单个Item宽度
      double itemWidth;
    
      //Item之间的间隙
      int itemSpace;
    
      //焦点
      FocusNode focusNode;
    
      TextEditingController controller;
    
      //线路画笔
      late Paint linePaint;
    
      //文字画笔
      late TextPainter textPainter;
    
      //当前文字索引
      int currentIndex = 0;
    
      //左右间距值
      double space = 0;
    
      CodeCustomPainter(
        this.maxLength,
        this.width,
        this.height,
        this.mode,
        this.style,
        this.codeBgColor,
        this.borderWidth,
        this.borderColor,
        this.borderSelectColor,
        this.borderRadius,
        this.contentColor,
        this.contentSize,
        this.itemWidth,
        this.itemSpace,
        this.focusNode,
        this.controller,
      ) {
        linePaint = Paint()
          ..color = borderColor
          ..isAntiAlias = true
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round
          ..strokeWidth = borderWidth;
    
        textPainter = TextPainter(
          textAlign: TextAlign.center,
          textDirection: TextDirection.ltr,
        );
      }
    
      @override
      void paint(Canvas canvas, Size size) {
        //当前索引(待输入的光标位置)
        currentIndex = controller.text.length;
        //Item宽度(这里判断如果设置了宽度并且合理就使用当前设置的宽度,否则平均计算)
        if (itemWidth != -1 &&
            (itemWidth * maxLength + itemSpace * (maxLength - 1)) <= width) {
          itemWidth = itemWidth;
        } else {
          itemWidth = ((width - itemSpace * (maxLength - 1)) / maxLength);
        }
    
        //计算左右间距大小
        space = (width - itemWidth * maxLength - itemSpace * (maxLength - 1)) / 2;
    
        //绘制样式
        switch (style) {
          //表格
          case CodeStyle.form:
            _drawFormCode(canvas, size);
            break;
          //方块
          case CodeStyle.rectangle:
            _drawRectangleCode(canvas, size);
            break;
          //横线
          case CodeStyle.line:
            _drawLineCode(canvas, size);
            break;
          //圈圈
          case CodeStyle.circle:
            _drawCircleCode(canvas, size);
            break;
          //TODO  拓展
        }
    
        //绘制文字内容
        _drawContentCode(canvas, size);
      }
    
      /*
       * 绘制表格样式
       */
      void _drawFormCode(Canvas canvas, Size size) {
        //绘制表格边框
        Rect rect = Rect.fromLTRB(space, 0, width - space, height);
        RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(borderRadius));
        linePaint.color = borderColor;
        canvas.drawRRect(rRect, linePaint);
    
        //绘制表格中间分割线
        for (int i = 1; i < maxLength; i++) {
          double startX = space + itemWidth * i + itemSpace * i;
          double startY = 0;
          double stopY = height;
          canvas.drawLine(Offset(startX, startY), Offset(startX, stopY), linePaint);
        }
    
        //绘制当前位置边框
        for (int i = 0; i < maxLength; i++) {
          if (currentIndex != -1 && currentIndex == i && focusNode.hasFocus) {
            //计算每个表格的左边距离
            double left = 0;
            if (i == 0) {
              left = (space + itemWidth * i);
            } else {
              left = ((space + itemWidth * i + itemSpace * i));
            }
            linePaint.color = borderSelectColor;
            //第一个
            if (i == 0) {
              RRect rRect = RRect.fromLTRBAndCorners(
                  left, 0, left + itemWidth, height,
                  topLeft: Radius.circular(borderRadius),
                  bottomLeft: Radius.circular(borderRadius));
              canvas.drawRRect(rRect, linePaint);
            }
            //最后一个
            else if (i == maxLength - 1) {
              RRect rRect = RRect.fromLTRBAndCorners(
                  left, 0, left + itemWidth, height,
                  topRight: Radius.circular(borderRadius),
                  bottomRight: Radius.circular(borderRadius));
              canvas.drawRRect(rRect, linePaint);
            }
            //其他
            else {
              RRect rRect =
                  RRect.fromLTRBAndCorners(left, 0, left + itemWidth, height);
              canvas.drawRRect(rRect, linePaint);
            }
          }
        }
      }
    
      /*
       * 绘制方块样式
       */
      void _drawRectangleCode(Canvas canvas, Size size) {
        for (int i = 0; i < maxLength; i++) {
          double left = 0;
          if (i == 0) {
            left = space + i * itemWidth;
          } else {
            left = space + i * itemWidth + itemSpace * i;
          }
          Rect rect = Rect.fromLTRB(left, 0, left + itemWidth, height);
          RRect rRect =
              RRect.fromRectAndRadius(rect, Radius.circular(borderRadius));
          //当前光标样式
          if (currentIndex != -1 && currentIndex == i && focusNode.hasFocus) {
            linePaint.color = borderSelectColor;
            canvas.drawRRect(rRect, linePaint);
          }
          //默认样式
          else {
            linePaint.color = borderColor;
            canvas.drawRRect(rRect, linePaint);
          }
        }
      }
    
      /*
       * 绘制横线样式
       */
      void _drawLineCode(Canvas canvas, Size size) {
        for (int i = 0; i < maxLength; i++) {
          //当前选中状态
          if (controller.value.text.length == i && focusNode.hasFocus) {
            linePaint.color = borderSelectColor;
          }
          //默认状态
          else {
            linePaint.color = borderColor;
          }
          double startX = space + itemWidth * i + itemSpace * i;
          double startY = height - borderWidth;
          double stopX = startX + itemWidth;
          double stopY = startY;
          canvas.drawLine(Offset(startX, startY), Offset(stopX, stopY), linePaint);
        }
      }
    
      /*
       * 绘制圈圈样式
       */
      void _drawCircleCode(Canvas canvas, Size size) {
        for (int i = 0; i < maxLength; i++) {
          //当前绘制的圆圈的左x轴坐标
          double left = 0;
          if (i == 0) {
            left = space + i * itemWidth;
          } else {
            left = space + i * itemWidth + itemSpace * i;
          }
          //圆心坐标
          double cx = left + itemWidth / 2.0;
          double cy = height / 2.0;
          //圆形半径
          double radius = itemWidth / 5.0;
          //默认样式
          if (i >= currentIndex) {
            linePaint.style = PaintingStyle.fill;
            canvas.drawCircle(Offset(cx, cy), radius, linePaint);
          }
        }
      }
    
      /*
       * 绘制验证码文字
       */
      void _drawContentCode(Canvas canvas, Size size) {
        String textStr = controller.text;
        for (int i = 0; i < maxLength; i++) {
          if (textStr.isNotEmpty && i < textStr.length) {
            switch (mode) {
              case CodeMode.text:
                String code = textStr[i].toString();
                textPainter.text = TextSpan(
                  text: code,
                  style: const TextStyle(color: Colors.red, fontSize: 30),
                );
                textPainter.layout();
                double x = space +
                    itemWidth * i +
                    itemSpace * i +
                    (itemWidth - textPainter.width) / 2;
                double y = (height - textPainter.height) / 2;
                textPainter.paint(canvas, Offset(x, y));
                break;
              //TODO  拓展
            }
          }
        }
      }
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) {
        return true;
      }
    }
    
    

    如果滑动到这里,别忙走,请关注下"Android小样"公众号呗。

    Github:https://github.com/yixiaolunhui/flutter_xy

    本文由mdnice多平台发布

    相关文章

      网友评论

        本文标题:Flutter-自定义短信验证码

        本文链接:https://www.haomeiwen.com/subject/oaldortx.html