Flutter版本的自定義短信驗證碼實現(xiàn)示例解析
效果圖(Flutter版本)
簡介
前幾天我發(fā)布了一個Android版本的短信驗證碼,今天發(fā)布Flutter版本,其實實現(xiàn)思路和原生版本是一模一樣,可以說是直接把原生的繪制代碼復(fù)制粘貼到Flutter項目中,kt修改為dart,實現(xiàn)樣式還是下面四種:
- 表格類型
- 方塊類型
- 橫線類型
- 圈圈類型
所以這里就不在闡述實現(xiàn)思路了,你也可以直接查看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) { //如果是表格樣式,就不設(shè)置Item之間的距離 if (style == CodeStyle.form) { itemSpace = 0; } } late int maxLength; //高度 late double height; //驗證碼模式 late CodeMode mode; //驗證碼樣式 late CodeStyle style; //背景色 late Color codeBgColor; //邊框?qū)挾? late double borderWidth; //邊框默認顏色 late Color borderColor; //邊框選中顏色 late Color borderSelectColor; //邊框圓角 late double borderRadius; //內(nèi)容顏色 late Color contentColor; //內(nèi)容大小 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, //設(shè)置最大長度 maxLength: widget.maxLength, //文字樣式為透明 style: const TextStyle( color: Colors.transparent, ), //只允許數(shù)據(jù)數(shù)字 inputFormatters: [ FilteringTextInputFormatter.digitsOnly, ], //彈出數(shù)字鍵盤 keyboardType: TextInputType.number, //邊框樣式取消 decoration: null, ), ); }, ), ); } } class CodeCustomPainter extends CustomPainter { double width; double height; int maxLength; //驗證碼模式 late CodeMode mode; //驗證碼樣式 late CodeStyle style; //背景色 Color codeBgColor; //邊框?qū)挾? double borderWidth; //邊框默認顏色 Color borderColor; //邊框選中顏色 Color borderSelectColor; //邊框圓角 double borderRadius; //內(nèi)容顏色 Color contentColor; //內(nèi)容大小 double contentSize; // 單個Item寬度 double itemWidth; //Item之間的間隙 int itemSpace; //焦點 FocusNode focusNode; TextEditingController controller; //線路畫筆 late Paint linePaint; //文字畫筆 late TextPainter textPainter; //當(dāng)前文字索引 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) { //當(dāng)前索引(待輸入的光標位置) currentIndex = controller.text.length; //Item寬度(這里判斷如果設(shè)置了寬度并且合理就使用當(dāng)前設(shè)置的寬度,否則平均計算) 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 拓展 } //繪制文字內(nèi)容 _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); } //繪制當(dāng)前位置邊框 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)); //當(dāng)前光標樣式 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++) { //當(dāng)前選中狀態(tài) if (controller.value.text.length == i && focusNode.hasFocus) { linePaint.color = borderSelectColor; } //默認狀態(tài) 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++) { //當(dāng)前繪制的圓圈的左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; } }
Github:https://github.com/yixiaolunhui/flutter_xy
以上就是Flutter版本的自定義短信驗證碼實現(xiàn)示例解析的詳細內(nèi)容,更多關(guān)于Flutter 自定義短信驗證碼的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
實例講解Android中ViewPager組件的一些進階使用技巧
這篇文章主要介紹了Android中ViewPager組件的一些進階使用技巧,包括添加標題與onPagerChangeListener監(jiān)聽使用等,需要的朋友可以參考下2016-03-03Android中FileProvider的各種場景應(yīng)用詳解
這篇文章主要為大家介紹了Android中FileProvider的各種場景應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09Android用PopupWindow實現(xiàn)自定義Dailog
這篇文章主要為大家詳細介紹了Android用PopupWindow實現(xiàn)自定義Dailog的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01詳解Android TextView屬性ellipsize多行失效的解決思路
這篇文章主要介紹了Android TextView屬性ellipsize多行失效的解決思路,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-07-07Okhttp3實現(xiàn)爬取驗證碼及獲取Cookie的示例
本篇文章主要介紹了Okhttp3實現(xiàn)爬取驗證碼及獲取Cookie的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10