实现一个像安卓彩蛋的隐藏功能

像安卓彩蛋一样的隐藏功能,通过多次点击可触发彩蛋

使用

添加依赖

1
2
flutter_egg: ^1.0.3

或者直接复制 lib/flutter_egg.dart 文件

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Egg(
neededNum: 5,
onTap: (int tapNum, int neededNum) {
if (tapNum > 1 && tapNum < 5) {
$warn('再按 ${neededNum - tapNum}次');
}
},
onTrigger: (int tapNum, int neededNum) {
$warn('yo~靓仔你发现了隐藏功能');
},
child: Text(
'You have pushed the\n button this many times:',
),
)

Getting Started

写项目的时候后端同学经常要我打一个测试包,但是又经常需要切换到线上地址查看效果,每次打个包要等几分钟实在太麻烦了.大家都知道安卓彩蛋是通过连续点击版本号来触发,于是便想做个像安卓彩蛋一样的隐藏功能,用来开启开发模式.

首先搭建简单的框架,可以点击,渲染 child

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
import 'package:flutter/material.dart';

class Egg extends StatefulWidget {
final Widget child;

Egg({
this.child,
});

@override
_EggState createState() => _EggState();
}

class _EggState extends State<Egg> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: handleOnTap,
child: widget.child,
);
}

void handleOnTap() {
print(111);
}
}

我们的组件想通用就不能写死数据,所以给类加上一些参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Egg extends StatefulWidget {
final Widget child;

/// 总共需要点击的次数
final int neededNum;

/// 两次点击的间隔
final Duration interval;

Egg({
this.child,
this.neededNum = 5,
this.interval = const Duration(seconds: 1),
});

@override
_EggState createState() => _EggState();
}

如何记录连续点击呢?只需要一个数组存储每次点击的时刻,通过对比本次和上次点击的时刻是否在容许间隔内,如果是则把本次时刻存起,用于下次对比;如果否就清空旧数据,只存本次点击时刻

下面是处理点击的逻辑.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
handleOnTap() {
DateTime _lastPressedAt = tapTimeList.length > 0 ? tapTimeList.last : null;
DateTime now = DateTime.now();

print(_lastPressedAt);

// 不超过点击间隔,tapTimeList 添加当前点击时刻
if (_lastPressedAt != null && (now.difference(_lastPressedAt) < widget.interval)) {
tapTimeList.add(now);
$warn('再按 ${widget.neededNum - tapTimeList.length}次');
} else {
// 两次点击间隔超过 时长限制,重新计时
print('超过 时长限制');
tapTimeList.clear();
tapTimeList.add(now);
}

print(tapTimeList);
print('---------');
}

到这一步我们已经可以实现连续点击,超时重置.

然后加一下外部传入点击回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef OnTapCallBack = void Function(int tapNum, int neededNum);

class Egg extends StatefulWidget {

...

final OnTapCallBack onTrigger;
final OnTapCallBack onTap;

Egg({

...

this.onTrigger,
this.onTap,
});

@override
_EggState createState() => _EggState();
}

完善点击和触发彩蛋的处理

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
  handleOnTap() {
DateTime lastPressedAt = tapTimeList.length > 0 ? tapTimeList.last : null;
DateTime now = DateTime.now();

// 不超过点击间隔,tapTimeList 添加当前点击时刻
if (lastPressedAt != null && (now.difference(lastPressedAt) < widget.interval)) {
tapTimeList.add(now);
// $warn('再按 ${widget.neededNum - tapTimeList.length}次');

// 到达条件,触发隐藏功能
if (tapTimeList.length >= widget.neededNum) {
if (widget.onTrigger != null) {
widget.onTrigger(tapTimeList.length, widget.neededNum);
}
// 清空记录点击列表
tapTimeList.clear();
}
} else {
// 两次点击间隔超过 时长限制,重新计时
tapTimeList.clear();
tapTimeList.add(now);
}

// 每次都触发的 tap 事件
if (widget.onTap != null) {
widget.onTap(tapTimeList.length, widget.neededNum);
}
}

这样一个朴实无华的彩蛋组件就完成了