1. 缘起
这两天在研究 CupertinoSliverRefreshControl
组件,使用中有个小细节吸引到了我的注意。在下拉达到一定程度时,会有 weng
的一声震动感。然后翻看源码中的具体实现逻辑,在下拉量大于 refreshTriggerPullDistance
时,会触发 HapticFeedback.mediumImpact();
方法。看到 Feedback
一词,也就知道这是震动反馈触发的方法了。
![](https://img.haomeiwen.com/i27762813/18980b2ef28688de.png)
2. HapticFeedback 类的介绍
HapticFeedback
非常简单,私有构造,且提供五个静态方法,很明显是一个工具类。
![](https://img.haomeiwen.com/i27762813/95e50281746ad512.png)
其中有五个方法,从体感上来说,五种方法的震感不同,其中 vibrate
和 lightImpact
感觉上没有太大差别。mediumImpact
相对较弱,heavyImpact
很弱,最后 selectionClick
不知道是我手机问题还是什么,似乎没有震感。
vibrate ≈ lightImpact > mediumImpact > heavyImpact > selectionClick
HapticFeedback
源码中介绍提到,这里的 API
故意设计的比较简洁,只是调用平台的默认行为,并不能达到精确控制系统震动模块目的。也就是说,这里就是简单震动一下,并无法精确控制振幅、震动时长等信息。
![](https://img.haomeiwen.com/i27762813/ccfd653c572c75f0.png)
对于 Android
来说,这五个方法分别对应 HapticFeedbackConstants
中的五个常量:
vibrate ---- HapticFeedbackConstants.LONG_PRESS
lightImpact ---- HapticFeedbackConstants.VIRTUAL_KEY
mediumImpact ---- HapticFeedbackConstants.KEYBOARD_TAP
heavyImpact ---- HapticFeedbackConstants.CONTEXT_CLICK (API 23+)
selectionClick ---- HapticFeedbackConstants.CLOCK_TICK
对于 ios
来是 10+
之后引入了新的震动反馈特性,很明显 Flutter
中 HapticFeedback
的方法名称是参照 ios
来命名的。
vibrate ---- kSystemSoundID_Vibrate
lightImpact ---- UIImpactFeedbackGenerator-UIImpactFeedbackStyleLight (ios10+)
mediumImpact ---- UIImpactFeedbackGenerator-UIImpactFeedbackStyleMedium (ios10+)
heavyImpact ---- UIImpactFeedbackGenerator-UIImpactFeedbackStyleHeavy (ios10+)
selectionClick ---- UISelectionFeedbackGenerator (ios10+)
3. HapticFeedback 的使用
因为都是静态方法,所以使用也是非常简单,调用一些即可,比如:
HapticFeedback.vibrate();
下面简单写个测试界面,通过点击按钮来触发不同的震动反馈。借此也来说一下,如何优雅地实现这种若干个需要触发事件的按钮。可能有人看到这个界面,就想到在 Wrap
放一个个的 ElevatedButton
不就行了吗。如果直接一个个塞进去,代码会很长,而且不易管理。
![](https://img.haomeiwen.com/i27762813/db1c045f16f345db.png)
其实这里的数据关系是 字符串和方法(函数)的映射
,而 方法(函数)
本身也可以作为对象。所以可以使用 Map
来维护数据,为了方便表示函数类型,可以通过 typedef
进行声明,比如下面的 VoidAsyncFunction
:
typedef VoidAsyncFunction = Future<void> Function();
final Map<String, VoidAsyncFunction> feedbackMap = const {
'vibrate': HapticFeedback.vibrate,
'heavyImpact': HapticFeedback.heavyImpact,
'mediumImpact': HapticFeedback.mediumImpact,
'lightImpact': HapticFeedback.lightImpact,
'selectionClick': HapticFeedback.selectionClick,
};
这样在 Wrap
中,通过 feedbackMap
来遍历 key
列表,生成 ElevatedButton
即可。其中 feedbackMap[name]
就是代表字符串名称对应的函数对象。
Wrap(
spacing: 5,
runSpacing: 5,
alignment: WrapAlignment.center,
children: feedbackMap.keys
.map((String name) => ElevatedButton(
onPressed: feedbackMap[name],
child: Text(name),
),
).toList(),
),
4. HapticFeedback 中的方法是异步的
HapticFeedback
中的方法是通过 SystemChannels.platform
执行平台方法实现功能的。也就是说,触发 vibrate
并不会立刻震动,向平台通道发送消息是个不确定时长的异步任务。
static Future<void> vibrate() async {
await SystemChannels.platform.invokeMethod<void>('HapticFeedback.vibrate');
}
如何你需要确切在震动之后才触发某段逻辑,可以通过 await
来等待异步任务完成。比如下面连续四次,间隔 500 ms
的震动。需要在前一次震动方法完成,才能开始下次震动。
void run() async {
Duration duration = const Duration(milliseconds: 500);
await HapticFeedback.vibrate();
await Future.delayed(duration);
await HapticFeedback.heavyImpact();
await Future.delayed(duration);
await HapticFeedback.mediumImpact();
await Future.delayed(duration);
await HapticFeedback.lightImpact();
}
不过一般来说,并没有必要非常精确地知道震动方法完成的时机,因为这个时间非常短,在 10 ms
左右。像下拉到一定高度给出震动感,并不是很在意确切的时间。
int tag = DateTime.now().millisecondsSinceEpoch;
await HapticFeedback.vibrate();
int now = DateTime.now().millisecondsSinceEpoch;
print(now-tag);
5. HapticFeedback 中各种震动在源码中的使用
首先在 android
和 fuchsia
中,长按事件会触发 vibrate
震动。iOS
平台一般不会对长按事件进行反馈。
![](https://img.haomeiwen.com/i27762813/62d9a51b87e2ef85.png)
另外,注意一点,在 InkWell
和 Tooltip
组件中才会触发 forLongPress
,也就是说 GestureDetector
的长按事件是没有震动反馈的。
![](https://img.haomeiwen.com/i27762813/8862ad436beef54b.png)
lightImpact
在 CupertinoSwitch
组件中被使用,只有在 iOS
平台才会有反馈。
![](https://img.haomeiwen.com/i27762813/9480b4d4cda7c984.png)
![](https://img.haomeiwen.com/i27762813/81fa1ad246768ffd.png)
mediumImpact
在 CupertinoSliverRefreshControl
和 CupertinoScrollbar
组件中被使用:
![](https://img.haomeiwen.com/i27762813/bfcec2f63b61d0ea.png)
selectionClick
在 CupertinoPicker
、LongPressDraggable
、CupertinoContextMenu
中被使用。
![](https://img.haomeiwen.com/i27762813/d6e91185b426011e.png)
最后,heavyImpact
方法没有在框架中被使用。这就是 HapticFeedback
关于震动反馈的一些小知识,本文就到这里,谢谢观看 ~
作者:张风捷特烈
链接:https://juejin.cn/post/7100733430383673381
来源:稀土掘金
网友评论