背景
在最近做的一个Flutter项目中,需要用到手势、指纹解锁,这种需求在原生应用中非常常见,但Flutter中手势密码解锁现有库比较少、官方也仅提供有一个 local_auth指纹库,所以就自己写了个手势库。
功能属性
- 支持自定义各状态下(常态、操作时、操作出错时、不可用时)线颜色、填充色和线宽;
- 支持自定义各种状态下(常态、操作时、操作出错时、不可用时)每个圆圈样式和连接线样式;
- 支持实心、空心两种样式
- 支持图案绘制完成后延迟 500毫秒(默认值)自动清除;
- 支持指示器辅助控件可选择使用;
- 业务逻辑(至少连点几个点、验证时最多可出错几次等)可自定义。
实现效果
思路
其实实现这个自定义的手势控件有很多思路,首先想到的是,要在View中创建9个圆,那么使用GridView再合适不过了,但是经过尝试,放弃了,这会使交互跟逻辑变的更加复杂,所以还是选择直接继承Widget,自己处理逻辑与手势,那么下面就是需要处理的逻辑:
- 根据控件的大小,绘制9个圆圈;
- 在手指按到圆圈时,开始绘制路线,并且将按下的圆圈置为选中状态;
- 在手指滑动时,绘制一根跟随手指移动的、起点为按下的圆圈的线;
- 当手指滑动到另外一个圆圈时,将第一个按下的圆圈与当前圆圈用线连起来,并且绘制一根以当前圆圈为起点的跟随手指移动的线;
- 手指按下到圆圈时,以及每次划过圆圈时,将此圆圈对应的数字添加到数组;
- 当手指抬起时,根据添加的数字判断密码是否正确,若错误,则将所有的线、选中的圆,都置为错误的状态、颜色;
- 当超过错误次数不可用时,将圆设为不可用状态、颜色,且不可滑动;
代码结构
类名 | 描述 |
---|---|
GestureUnlockView | 手势绘制区域、包括9个圆圈、手势操作、自定义属性等 |
UnlockPointPainter | 圆圈绘制类 |
UnlockPoint | 圆圈属性类 |
UnlockLinePainter | 连接线绘制类 |
GestureUnlockIndicator | 辅助指示器类 |
支持属性
属性名 | 作用 | 默认值 |
---|---|---|
size | 整个控件大小 | |
type | 圆圈类型 | 实心 |
padding | 与父 widget 边距 | 10 |
roundSpace | 圆圈之间的间距 | |
roundSpaceRatio | 圆圈之间的间距比例(以圆半径作为基准),[roundSpace]设置时无效 | 0.6 |
defaultColor | 常态时颜色 | Colors.grey |
selectedColor | 操作时选中颜色 | Colors.blue |
failedColor | 操作出错时颜色 | Colors.blue |
disableColor | 不可用时颜色 | Colors.grey |
lineWidth | 连接线宽度 | 2 |
solidRadiusRatio | 实心圆半径比例(以圆半径作为基准) | 0.4 |
touchRadiusRatio | 触摸有效区半径比例(以圆半径作为基准) | 0.6 |
delayTime | 延迟还原显示时间 | 500ms |
onCompleted | 手势绘制完成回调 |
使用示例
Step1:在项目pubspec.yaml添加依赖
dependencies:
flutter_gesture_unlock: ^1.0.6
Step2 初始化控件
GestureUnlockView(
size: LcfarmSize.dp(331), //设置大点,让触摸区域变大。让触摸更有效。
padding: LcfarmSize.dp(40),
roundSpace: LcfarmSize.dp(40),
defaultColor: LcfarmColor.colorE3E4E6,
selectedColor: LcfarmColor.color3776E9,
failedColor: LcfarmColor.colorFF5656,
disableColor: LcfarmColor.color30E3E4E6,
solidRadiusRatio: 0.3,
lineWidth: LcfarmSize.dp(2),
touchRadiusRatio: 0.3,
onCompleted: _gestureComplete,
);
void _gestureComplete(List<int> selected, UnlockStatus status) async {
switch (_notifier.status) {
case GestureStatus.create:
case GestureStatus.createFailed:
if (selected.length < 4) {
_notifier.setStatus(
status: GestureStatus.createFailed,
gestureTxt: "连接数不能小于4个,请重新设置",
);
_gestureUnlockView.updateStatus(UnlockStatus.failed);
} else {
_notifier.setStatus(
status: GestureStatus.verify,
gestureTxt: "请再次绘制解锁密码",
resetGestureTxt: "点击此处以重新开始",
);
_gesturePassword = GestureUnlockView.selectedToString(selected);
_gestureUnlockView.updateStatus(UnlockStatus.success);
_indicator.setSelectPoint(selected);
}
break;
case GestureStatus.verify:
case GestureStatus.verifyFailed:
String password = GestureUnlockView.selectedToString(selected);
if (_gesturePassword == password) {
showTips("手势密码设置成功");
//保存密码
} else {
_notifier.setStatus(
status: GestureStatus.verifyFailed,
gestureTxt: "与上一次绘制不一致, 请重新绘制",
);
_gestureUnlockView.updateStatus(UnlockStatus.failed);
}
break;
case GestureStatus.verifyFailedCountOverflow:
break;
}
}
指纹
由于官方插件库已经提供有 local_auth 库,在这里就不大赘述,具体使用就参考Flutter官方local_auth插件库。
最后
如果在使用过程遇到问题,欢迎下方留言交流。