-
-
Notifications
You must be signed in to change notification settings - Fork 511
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
501 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
lib/widget/gesture_password/gesture_password_demo_page.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:gsy_flutter_demo/widget/gesture_password/gesture_password_view.dart'; | ||
import 'package:gsy_flutter_demo/widget/link_sliver/link_flexible_space_bar.dart'; | ||
|
||
class GesturePasswordDemoPage extends StatefulWidget { | ||
const GesturePasswordDemoPage({super.key}); | ||
|
||
@override | ||
_GesturePasswordDemoState createState() => _GesturePasswordDemoState(); | ||
} | ||
|
||
class _GesturePasswordDemoState extends State<GesturePasswordDemoPage> { | ||
String _pwd = ''; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
title: const Text("手势密码"), | ||
), | ||
body: Center( | ||
child: Column( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
children: [ | ||
SizedBox( | ||
width: 300, | ||
height: 300, | ||
child: GesturePasswordView( | ||
pathWidth: 6, | ||
frameRadius: 30, | ||
onDone: (value) { | ||
setState(() { | ||
_pwd = value.join(); | ||
}); | ||
}, | ||
), | ||
), | ||
Text("当前密码: $_pwd"), | ||
], | ||
), | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import 'package:flutter/material.dart'; | ||
|
||
import 'src/gesture_view_controller.dart'; | ||
import 'src/gesture_view_path.dart'; | ||
import 'src/gesture_view_point.dart'; | ||
|
||
class GesturePasswordView extends StatefulWidget { | ||
|
||
/// 圆圈半径 | ||
final double frameRadius; | ||
|
||
/// 圆圈中心点半径 | ||
final double pointRadius; | ||
|
||
/// 圆圈普通状态下颜色 | ||
final Color color; | ||
|
||
/// 圆圈选中颜色 | ||
final Color highlightColor; | ||
|
||
/// 连线颜色 | ||
final Color pathColor; | ||
|
||
/// 连线半径 | ||
final double pathWidth; | ||
|
||
/// 手势结果 | ||
final Function(List<int>)? onDone; | ||
|
||
const GesturePasswordView({ | ||
super.key, | ||
this.pointRadius = 10, | ||
this.frameRadius = 40, | ||
this.color = Colors.grey, | ||
this.highlightColor = Colors.blue, | ||
this.pathColor = Colors.blue, | ||
this.onDone, | ||
this.pathWidth = 5, | ||
}); | ||
|
||
@override | ||
State<StatefulWidget> createState() => _GesturePasswordState(); | ||
} | ||
|
||
class _GesturePasswordState extends State<GesturePasswordView> { | ||
final GestureViewController controller = GestureViewController(); | ||
|
||
@override | ||
void initState() { | ||
controller.initParameters( | ||
pointRadius: widget.pointRadius, | ||
frameRadius: widget.frameRadius, | ||
color: widget.color, | ||
highlightColor: widget.highlightColor, | ||
pathColor: widget.pathColor, | ||
onFinishGesture: widget.onDone, | ||
pathWidth: widget.pathWidth, | ||
updateView: (){ | ||
setState(() {}); | ||
} | ||
); | ||
WidgetsBinding.instance.addPostFrameCallback((_) => controller.setPointValues()); | ||
super.initState(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return SizedBox( | ||
key: controller.globalKey, | ||
width: double.infinity, | ||
height: double.infinity, | ||
child: Stack( | ||
children: [ | ||
GestureDotsPanelWidget(points: controller.point), | ||
GestureViewPathWidget( | ||
points: controller.pathPoint, | ||
pathWidth: controller.pathWidth, | ||
color: controller.pathColor, | ||
onPanDown: controller.onPanDown, | ||
onPanEnd: controller.onPanEnd, | ||
onPanUpdate: controller.onPanUpdate, | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} |
141 changes: 141 additions & 0 deletions
141
lib/widget/gesture_password/src/gesture_view_controller.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import 'package:flutter/material.dart'; | ||
|
||
import 'gesture_view_model.dart'; | ||
|
||
class GestureViewController { | ||
final List<GesturePasswordPointModel> _points = []; | ||
final List<Offset> _pathPoint = []; | ||
final GlobalKey globalKey = GlobalKey(); | ||
double _frameRadius = 0.0; | ||
double _pointRadius = 0.0; | ||
Color _color = Colors.grey; | ||
Color _highlightColor = Colors.blue; | ||
Color _pathColor = Colors.blue; | ||
Function(List<int>)? _onFinishGesture; | ||
double _pathWidth = 5; | ||
Function()? _updateView; | ||
Offset? _firstPoint; | ||
Offset? _movePoint; | ||
List<int> _result = []; | ||
} | ||
|
||
extension Data on GestureViewController { | ||
List<GesturePasswordPointModel> get point => _points; | ||
|
||
List<Offset> get pathPoint { | ||
List<Offset> tempPathPoint = []; | ||
tempPathPoint.addAll(_pathPoint); | ||
if (_movePoint != null) { | ||
tempPathPoint.add(_movePoint!); | ||
} | ||
return tempPathPoint; | ||
} | ||
|
||
Color get pathColor => _pathColor; | ||
|
||
double get pathWidth => _pathWidth; | ||
|
||
} | ||
|
||
extension Private on GestureViewController { | ||
void _initPoint() { | ||
_points.addAll(List.generate( | ||
9, | ||
(index) => GesturePasswordPointModel( | ||
index: index, | ||
frameRadius: _frameRadius, | ||
pointRadius: _pointRadius, | ||
color: _color, | ||
highlightColor: _highlightColor, | ||
pathColor: _pathColor, | ||
), | ||
)); | ||
} | ||
|
||
double _getPointWidth(double width) => width / 3; | ||
} | ||
|
||
extension Public on GestureViewController { | ||
void initParameters({ | ||
double frameRadius = 0.0, | ||
double pointRadius = 0.0, | ||
Color color = Colors.grey, | ||
Color highlightColor = Colors.blue, | ||
Color pathColor = Colors.blue, | ||
Function(List<int>)? onFinishGesture, | ||
Function()? updateView, | ||
double pathWidth = 5, | ||
}) { | ||
_frameRadius = frameRadius; | ||
_pointRadius = pointRadius; | ||
_color = color; | ||
_highlightColor = highlightColor; | ||
_pathColor = pathColor; | ||
_onFinishGesture = onFinishGesture; | ||
_updateView = updateView; | ||
_pathWidth = pathWidth; | ||
_initPoint(); | ||
} | ||
|
||
void setPointValues() { | ||
try { | ||
Size size = globalKey.currentContext?.size ?? Size.zero; | ||
double pointWidth = _getPointWidth(size.width); | ||
List<Offset> pointCenter = []; | ||
for (int x = 1; x <= 3; x++) { | ||
for (int y = 1; y <= 3; y++) { | ||
Offset center = Offset((y - 1) * pointWidth + pointWidth / 2, | ||
(x - 1) * pointWidth + pointWidth / 2); | ||
pointCenter.add(center); | ||
} | ||
} | ||
for (int index = 0; index < pointCenter.length; index++) { | ||
_points[index].centerPoint = pointCenter[index]; | ||
} | ||
} catch (_) {} | ||
} | ||
} | ||
|
||
extension Tap on GestureViewController { | ||
void onPanDown(DragDownDetails e) { | ||
for (var item in _points) { | ||
if (item.containPoint(e.localPosition)) { | ||
item.selected = true; | ||
_firstPoint = e.localPosition; | ||
_pathPoint.add(item.centerPoint); | ||
_updateView?.call(); | ||
_result.add(item.index); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void onPanUpdate(DragUpdateDetails e) { | ||
if (_firstPoint == null) return; | ||
_movePoint = e.localPosition; | ||
for (var item in _points) { | ||
if (item.containPoint(e.localPosition)) { | ||
if (!item.selected) { | ||
item.selected = true; | ||
_pathPoint.add(item.centerPoint); | ||
_result.add(item.index); | ||
} | ||
break; | ||
} | ||
} | ||
_updateView?.call(); | ||
} | ||
|
||
void onPanEnd(DragEndDetails e) { | ||
_firstPoint = null; | ||
_movePoint = null; | ||
|
||
_onFinishGesture?.call(_result); | ||
_result.clear(); | ||
for (var element in _points) { | ||
element.selected = false; | ||
} | ||
_pathPoint.clear(); | ||
_updateView?.call(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
|
||
import 'dart:math'; | ||
import 'package:flutter/material.dart'; | ||
|
||
class GesturePasswordPointModel { | ||
Offset centerPoint = Offset.zero; | ||
bool selected = false; | ||
double frameRadius; | ||
double pointRadius; | ||
Color color = Colors.grey; | ||
Color highlightColor = Colors.blue; | ||
Color pathColor = Colors.blue; | ||
int index = 0; | ||
|
||
GesturePasswordPointModel({ | ||
this.index = 0, | ||
this.centerPoint = Offset.zero, | ||
this.frameRadius = 0.0, | ||
this.pointRadius = 0.0, | ||
this.selected = false, | ||
this.color = Colors.grey, | ||
this.highlightColor = Colors.blue, | ||
this.pathColor = Colors.blue, | ||
}); | ||
|
||
|
||
Color get pointColor{ | ||
return selected ? highlightColor : color; | ||
} | ||
Color get frameColor => selected ? highlightColor : color; | ||
|
||
bool containPoint(Offset offset){ | ||
return distanceTo(offset, centerPoint) <= frameRadius; | ||
} | ||
|
||
|
||
double distanceTo(Offset f1, Offset f2){ | ||
var dx= f1.dx - f2.dx; | ||
var dy= f1.dy - f2.dy; | ||
return sqrt(dx * dx + dy * dy); | ||
} | ||
|
||
} |
Oops, something went wrong.