1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
| import 'dart:async'; import 'dart:ui'; import 'package:flutter/material.dart'; class GesturePWDPage extends StatefulWidget { GesturePWDPage({Key key}) : super(key: key);
@override _GesturePWDPageState createState() => _GesturePWDPageState(); } class _GesturePWDPageState extends State<GesturePWDPage> { String password = ""; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("test"), ), body: Column( children: <Widget>[ SizedBox(height: 20,), Text("result: $password",style: TextStyle(color: Colors.red,fontSize: 32),), SizedBox(height: 20,), Container( color: Colors.white, alignment: Alignment.center, child: GesturePwdModel( width: MediaQueryData.fromWindow(window).size.width, height: MediaQueryData.fromWindow(window).size.width, onSuccess: (value) { setState(() { password = value; }); }, onError: () { setState(() { password = "密码长度小于4位"; }); }, ), ) ], ) ); } }
/* 手势密码 */ class GesturePwdModel extends StatefulWidget { final double width; final double height; final Color unSelectedColor; final Color selectedColor; final Color selectedCenterColor; final double circleSize; final double circleCenterSize; final Color lineColor; final double lineWidth; final ValueChanged<String> onSuccess; final VoidCallback onError; final int minLineLength; final int showTimes;
GesturePwdModel({Key key, this.width, this.height, this.unSelectedColor = Colors.grey, this.selectedColor = Colors.greenAccent, this.selectedCenterColor = Colors.green, this.circleSize = 40.0, this.circleCenterSize = 16.0, this.lineColor = Colors.green, this.lineWidth = 4.0, this.minLineLength = 4, this.showTimes = 1000, @required this.onSuccess, this.onError }): super(key: key);
@override _GesturePwdModelState createState() => _GesturePwdModelState(); }
class _GesturePwdModelState extends State<GesturePwdModel> { List<Circle> circleList = []; //每个的圆心 List<Circle> lineList = []; //连线的拐点 Offset nowMoveXY; Timer _timer;
@override void initState() { /* 确定九个点的圆心 */ double width = widget.width??MediaQueryData.fromWindow(window).size.width; double itemWidthHalf = width / 6; double height = widget.height??MediaQueryData.fromWindow(window).size.width; double itemHeightHalf = height / 6; //每个圆的中心点 for (int i = 0; i < 9; i++) { double coordinateX = (2 * (i % 3) + 1) * itemWidthHalf; //x为2n-1的位置 double coordinateY = (2 * ((i ~/ 3)) +1) * itemHeightHalf; circleList.add(Circle(Offset(coordinateX, coordinateY),i)); } super.initState(); }
/* 手指按下或移动 */ headDownOrMove(details) { setState(() { RenderBox box = context.findRenderObject(); nowMoveXY = box.globalToLocal(details.globalPosition); if (nowMoveXY.dy < 0) { nowMoveXY = Offset(nowMoveXY.dx, 0.0); } if (nowMoveXY.dy > widget.height) { nowMoveXY = Offset(nowMoveXY.dx, widget.height); } for (var i = 0; i < circleList.length; i++) { Offset cross = nowMoveXY - circleList[i].offset; if(!circleList[i].isSelected){ if (cross.dx.abs() < (widget.circleSize??40.0) / 2 && cross.dy.abs() < (widget.circleSize??40.0) / 2) { circleList[i].isSelected = true; lineList.add(circleList[i]); if(lineList.length == 9){ successBack(); } } } } }); }
/* 松手指或者连接9个回调 */ successBack() { if(lineList.length < widget.minLineLength){ widget.onError != null?widget.onError():print(lineList.length); }else{ String result = ""; for (int i = 0; i < lineList.length; i++) { result += "${lineList[i].index}"; } widget.onSuccess(result); } startTimer(); }
startTimer() { _timer = Timer.periodic(Duration(milliseconds: widget.showTimes), (timer){ setState(() { lineList.clear(); circleList.forEach((element) { element.isSelected = false; }); }); timer.cancel(); }); }
@override void dispose() { if(_timer != null){ _timer.cancel(); } super.dispose(); }
@override Widget build(BuildContext context) { return GestureDetector( onPanDown: (DragDownDetails details) { headDownOrMove(details); }, onPanUpdate: (DragUpdateDetails details) { if(lineList.length < 9){ headDownOrMove(details); } }, onPanEnd: (DragEndDetails details) { successBack(); }, child: CustomPaint( size: Size(widget.width??MediaQuery.of(context).size.width, widget.height??MediaQuery.of(context).size.width), painter: MyPainter( circleList, lineList, nowMoveXY, unSelectedColor: widget.unSelectedColor, selectedColor: widget.selectedColor, selectedCenterColor: widget.selectedCenterColor, circleSize: widget.circleSize, circleCenterSize: widget.circleCenterSize, lineColor: widget.lineColor, lineWidth: widget.lineWidth, ), ) ); } }
class MyPainter extends CustomPainter { List<Circle> circleList; List<Circle> lineList; Color unSelectedColor; //未选中颜色 Color selectedColor; //选中圆的颜色 Color selectedCenterColor; //选中圆心颜色 double circleSize; //圆宽度 double circleCenterSize; //圆心宽度 Color lineColor; //线颜色 double lineWidth; //线宽度 Offset nowMoveXY; //现在的手指位置
MyPainter( this.circleList, this.lineList, this.nowMoveXY, { this.unSelectedColor, this.selectedColor, this.selectedCenterColor, this.circleSize, this.circleCenterSize, this.lineColor, this.lineWidth, } );
@override void paint(Canvas canvas, Size size) {
/* 未选中 */ var unSelectPanint = Paint() ..style = PaintingStyle.fill ..color = unSelectedColor;
/* 选中颜色 */ var selectPanint = Paint() ..style = PaintingStyle.fill ..color = selectedColor;
/* 选中圆心颜色 */ var selectCenterPanint = Paint() ..style = PaintingStyle.fill ..color = selectedCenterColor;
//线 final linePaint = new Paint() ..color = lineColor ..style = PaintingStyle.fill ..strokeWidth = lineWidth;
//画圆 for (int i = 0; i < circleList.length; i++) { Circle circle = circleList[i]; if(circle.isSelected){ canvas.drawCircle(circle.offset, circleSize, selectPanint); canvas.drawCircle(circle.offset, circleCenterSize, selectCenterPanint); }else{ canvas.drawCircle(circle.offset, circleSize, unSelectPanint); } } /* 画线 */ if (lineList.length > 0) { for (int i = 0; i < lineList.length; i++) { canvas.drawLine( lineList[i].offset, i == lineList.length - 1 ? nowMoveXY : lineList[i + 1].offset, linePaint ); } } }
@override bool shouldRepaint(CustomPainter oldDelegate) => true;}
class Circle{ Offset offset; //(x,y) bool isSelected = false; //是否被选中 int index; //第几个小圆点
Circle(this.offset, this.index); }
|