基于Flutter實(shí)現(xiàn)手勢(shì)密碼加密與解鎖功能
前言
密碼的由來:在公元前405年,由古希臘和斯巴達(dá)的戰(zhàn)爭(zhēng)中,由于斯巴達(dá)盟友波斯帝國背叛,導(dǎo)致古希臘和斯巴達(dá)兩敗俱傷,這時(shí)斯巴達(dá)抓了一個(gè)波斯國的信使,這個(gè)信使 沒有任何情報(bào),只有一條有著雜亂無章的希臘字母的普通腰帶,最終斯巴達(dá)統(tǒng)帥破解了這條腰帶,成功擊敗了希臘。這就是世界上最早的密碼。同時(shí)也是世界上最早的解密。
密碼在我們生活中無處不在,作為個(gè)人隱私的最后一道防線顯得無比的重要,現(xiàn)在世界有各種各樣形形色色的密碼,解密方式也是層出不窮,加密,解密的過程里充滿了數(shù)學(xué)以及計(jì)算機(jī)的知識(shí),而現(xiàn)在手機(jī)的加密的方式也有很多、數(shù)字、手勢(shì)、指紋、人臉、虹膜等等方式多種多樣,有加密就有解密、有破譯等等,有些App為了安全起見,例如招商銀行的銀行類的App在每次啟動(dòng)的時(shí)候都需要解鎖驗(yàn)證身份,那么今天我們用Flutter實(shí)現(xiàn)一個(gè)其中的加密方式,手勢(shì)密碼。
知識(shí)點(diǎn):手勢(shì)識(shí)別、繪制、動(dòng)畫
1、繪制靜態(tài)圖形
手勢(shì)密碼一般都是九宮格的形狀,所以第一步我們先把這個(gè)輔助矩形九宮格畫出來,然后在每一個(gè)格子內(nèi)就可以繪制我們喜歡的圖形了,九宮格那就很簡(jiǎn)單了。
首先我們定義一個(gè)正方形300*300 為九宮格的區(qū)域,然后對(duì)這個(gè)正方形平均分成9個(gè)小格子,我們需要找到這9個(gè)小格子的中心點(diǎn)來繪制九宮格,那么我們找到之后把每個(gè)小格子的中心用一個(gè)List存儲(chǔ)起來方便以后繪制。
代碼:
double size = 300;// 正方形邊長(zhǎng) List<Offset> centerOffset = <Offset>[];// 九宮格中心點(diǎn) // 上面3個(gè) centerOffset.add(Offset(-size / 3, -size / 3)); centerOffset.add(Offset(-size / 3 + size / 3, -size / 3)); centerOffset.add(Offset(-size / 3 + size / 3 * 2, -size / 3)); // 中間3個(gè) centerOffset.add(Offset(-size / 3, 0)); centerOffset.add(Offset(-size / 3 + size / 3, 0)); centerOffset.add(Offset(-size / 3 + size / 3 * 2, 0)); // 下面3個(gè) centerOffset.add(Offset(-size / 3, size / 3)); centerOffset.add(Offset(-size / 3 + size / 3, size / 3)); centerOffset.add(Offset(-size / 3 + size / 3 * 2, size / 3));
接下來我們就可以用這些點(diǎn)繪制九宮格了。
核心代碼:
Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2
..color = Colors.black87;
canvas.drawRect(
Rect.fromCenter(center: Offset.zero, width: 300, height: 300), paint);
// 繪制輔助區(qū)域
_drawHelpRect(canvas, size, paint);
void _drawHelpRect(Canvas canvas, Size size, Paint paint) {
for (int i = 0; i < centerOffset.length; i++) {
canvas.drawRect(
Rect.fromCenter(center: centerOffset[i], width: 100, height: 100),
paint);
}
}效果圖:

得到九宮格之后,我們看到很多手勢(shì)解鎖小格子內(nèi)都是圓形,這里我們繼續(xù)在每個(gè)小格子繪制圓形圖案,
核心代碼:
// 繪制圓
_drawCirCle(canvas, size, paint);
void _drawCirCle(Canvas canvas, Size size, Paint paint) {
for (int i = 0; i < centerOffset.length; i++) {
canvas.drawCircle(centerOffset[i], 30, paint..color = Colors.black87);
}
}效果圖:

到這里基本圖形已經(jīng)繪制完了。
2、存儲(chǔ)手勢(shì)密碼數(shù)據(jù)
首先我們看下手勢(shì)解鎖一共有按下、移動(dòng)、抬起三個(gè)動(dòng)作組成,在移動(dòng)的過程中會(huì)經(jīng)過九宮格內(nèi)部的圓形區(qū)域,也就是說,在經(jīng)過圓形區(qū)域的時(shí)候,我們需要將這個(gè)數(shù)據(jù)保存下來然后通知畫布進(jìn)行更新,這里的邏輯跟我之前一篇繪制海豚那篇原理一樣,首先創(chuàng)建UnlockController類繼承ChangeNotifier。這里我們將九宮格中的每一個(gè)小格子對(duì)應(yīng)一個(gè)數(shù)字進(jìn)行封裝一下。也是之后的設(shè)置和解鎖需要保存的數(shù)據(jù)。 這樣的話我們上面的九宮格就需要修改一下了,將Offset改為PassWord就行。
數(shù)據(jù)代碼:
class UnlockController extends ChangeNotifier {
// 存儲(chǔ)按壓的點(diǎn)集合
List<PassWord> _points = [];
List<PassWord> get points => _points;
// 當(dāng)前手指的位置
Offset? _currentOffset;
Offset? get currentOffset => _currentOffset;
//
set currentOffset(Offset? value) {
_currentOffset = value;
notifyListeners();
}
addPoint(PassWord offset) {
_points.add(offset);
notifyListeners();
}
// 清除所有點(diǎn)
clearAllPoint() {
_points.clear();
notifyListeners();
}
}
class PassWord {
int num; // 密碼數(shù)字
Offset offset; // 密碼數(shù)字對(duì)應(yīng)的點(diǎn)
PassWord(this.num, this.offset);
}有了這些數(shù)據(jù)之后我們接下來就要跟手勢(shì)進(jìn)行交互了。
3、添加手勢(shì)交互
接著上面剛說的,交互一共有三種狀態(tài),按下、移動(dòng)、抬起,那么我們就先對(duì)這三種狀態(tài)進(jìn)行監(jiān)聽,
GestureDetector(
child: CustomPaint(
size: Size(size, size),
painter:
_GesturesUnlockPainter(_unlockController, centerOffset),
),
onPanDown: (d) {
// 手指按下
},
onPanUpdate: (d) {
// 移動(dòng)
},
onPanEnd: (d) {
// 抬起結(jié)束
},
)交互思路: 手勢(shì)密碼的特點(diǎn)是每個(gè)九宮格只允許點(diǎn)亮一次,再次經(jīng)過不保存數(shù)據(jù)。
按下: 判斷是否在九宮格內(nèi)部任一圓形區(qū)域內(nèi),在:點(diǎn)亮圓形,保存數(shù)據(jù),不在:不做任何操作,
移動(dòng): 判斷是否移動(dòng)到九宮格任一圓形區(qū)域內(nèi),在:判斷是否已點(diǎn)亮-未點(diǎn)亮:點(diǎn)亮圓形,保存數(shù)據(jù),已點(diǎn)亮:不做任何操作,不在區(qū)域內(nèi),也不做數(shù)據(jù)任何操作。
抬起: 獲取密碼,進(jìn)行設(shè)置或驗(yàn)證。
大概思路還是比較清晰的,下面我們就對(duì)這些判斷條件進(jìn)行判斷。
代碼:
///手指按下、移動(dòng)觸發(fā)
void judgeZone(Offset src) {
/// 循環(huán)所有的九宮格
for (int i = 0; i < centerOffset.length; i++) {
var srcTranslate = src.translate(-size / 2, -size / 2);
// 判斷手指按的位置是否在九宮格圓形區(qū)域
if (judgeCircleArea(srcTranslate, centerOffset[i].offset, 30)) {
// 在 判斷是否已添加
for (int j = 0; j < _unlockController.points.length; j++) {
if (_unlockController.points[j] == centerOffset[i]) {
// 已添加過 返回
return;
}
}
// 未添加過 進(jìn)行添加
_unlockController.addPoint(centerOffset[i]);
return;
}
}
// 無點(diǎn)
}
///判斷出是否在某點(diǎn)的半徑為r圓范圍內(nèi)
bool judgeCircleArea(Offset src, Offset dst, double r)=>(src - dst).distance <= r;有了手指數(shù)據(jù)之后,我們就能根據(jù)這些數(shù)據(jù)的變化進(jìn)而通知畫布進(jìn)行更新了。
4、繪制、刷新密碼線
上面我們已經(jīng)拿到數(shù)據(jù)了,接下來我們進(jìn)行繪制密碼線,如何刷新畫布之前的文章我已經(jīng)講過很多遍了,這里不熟悉的同學(xué)可以看下之前繪制小海豚那一篇文章貝塞爾曲線繪制一個(gè)小海豚,回歸正題,既然我們拿到了手指經(jīng)過的九宮格的中心點(diǎn),那繪制線就變得非常容易了,
核心代碼:
var offsets = unlockController.points.map((e) => e.offset).toList();
// 繪制按壓點(diǎn)
canvas.drawPoints(
PointMode.points,
offsets,
paint
..strokeWidth = 20
..strokeCap = StrokeCap.round
..color = Colors.red);
// 繪制密碼
Path path = Path();
if (unlockController.points.isNotEmpty) {
path.moveTo(unlockController.points[0].offset.dx,
unlockController.points[0].offset.dy);
if (unlockController.currentOffset != null) {
// 繪制當(dāng)前手勢(shì)線 未經(jīng)過九宮格圓形時(shí)
canvas.drawLine(unlockController.points.last.offset,
unlockController.currentOffset!, paint..strokeWidth = 2);
}
}
for (int i = 1; i < unlockController.points.length; i++) {
path.lineTo(unlockController.points[i].offset.dx,
unlockController.points[i].offset.dy);
}
canvas.drawPath(path, paint..strokeWidth = 2);這樣我們就把手勢(shì)經(jīng)過的密碼線繪制出來了。
效果圖:

5、加入密碼錯(cuò)誤動(dòng)畫
當(dāng)我們輸入密碼錯(cuò)誤時(shí),給用戶一個(gè)提示密碼錯(cuò)誤的交互也是很有必要的,那么接下來我們就添加一個(gè)簡(jiǎn)單文字抖動(dòng)效果提示用戶密碼輸入錯(cuò)誤。
添加動(dòng)畫也很簡(jiǎn)單,思路: 不斷改變文字的邊距從而達(dá)到抖動(dòng)效果。
核心代碼:
late AnimationController _animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
late CurvedAnimation curvedAnimation =
CurvedAnimation(curve: Curves.easeIn, parent: _animationController);
late Animation<double> animation =
Tween(begin: 0.0, end: 10.0).animate(curvedAnimation)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.reset();
}
});
AnimatedBuilder(
animation: animation,
builder: (ctx, child) {
return Container(
margin: EdgeInsetsDirectional.only(
bottom: 20,
start: _errorPwd() * 20,
end: animation.value),
child: Text(
text,
style: TextStyle(fontSize: 20, color: textColor),
),
);
}),
double _errorPwd() {
double x = animation.value; // 變化速度 0-10,
double d = x - x.truncate(); // 獲取這個(gè)數(shù)字的小數(shù)部分
double? y;
if (d <= 0.5) {
y = 2 * d;
} else {
y = 1 - 2 * (d - 0.5);
}
return y;
}假設(shè)我們?cè)O(shè)置的密碼是14789,也就是L型,看下最終效果:

完整源碼之后我會(huì)放到github上。
總結(jié)
通過手勢(shì)識(shí)別我們用到了App中核心的三大框架,手勢(shì)、繪制、以及動(dòng)畫中的知識(shí)。其實(shí)原理也并不復(fù)雜,只要掌握了這三大框架的核心基礎(chǔ)知識(shí)
到此這篇關(guān)于基于Flutter實(shí)現(xiàn)手勢(shì)密碼加密與解鎖功能的文章就介紹到這了,更多相關(guān)Flutter手勢(shì)密碼加密解鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android RecyclerView仿新聞?lì)^條的頻道管理功能
這篇文章主要介紹了Android RecyclerView仿新聞?lì)^條的頻道管理功能,需要的朋友可以參考下2017-06-06
Android自定義Dialog實(shí)現(xiàn)加載對(duì)話框效果
這篇文章將介紹如何定制當(dāng)今主流的對(duì)話框,通過自定義dialog實(shí)現(xiàn)加載對(duì)話框效果,具體實(shí)現(xiàn)代碼大家通過本文學(xué)習(xí)吧2018-05-05
如何在Android中實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Http服務(wù)器
這篇文章主要介紹了如何在Android中實(shí)現(xiàn)一個(gè)簡(jiǎn)易的Http服務(wù)器,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
Android應(yīng)用退出登錄的實(shí)現(xiàn)方法
每一個(gè)app都會(huì)有一個(gè)”退出登陸”的功能,當(dāng)點(diǎn)擊退出之后需要將所有的Activity都finish掉,開始是想將棧中的所有Activity清除掉,但是沒有找到方法,后來用廣播實(shí)現(xiàn)了。下面小編給大家分享android應(yīng)用退出登錄的實(shí)現(xiàn)方法,需要的朋友參考下2017-04-04

