likes
comments
collection
share

【Flutter 小知识】震动反馈 HapticFeedback

作者站长头像
站长
· 阅读数 11

1. 缘起

这两天在研究 CupertinoSliverRefreshControl 组件,使用中有个小细节吸引到了我的注意。在下拉达到一定程度时,会有 weng 的一声震动感。然后翻看源码中的具体实现逻辑,在下拉量大于 refreshTriggerPullDistance 时,会触发 HapticFeedback.mediumImpact(); 方法。看到 Feedback 一词,也就知道这是震动反馈触发的方法了。

【Flutter 小知识】震动反馈 HapticFeedback


2. HapticFeedback 类的介绍

HapticFeedback 非常简单,私有构造,且提供五个静态方法,很明显是一个工具类。

【Flutter 小知识】震动反馈 HapticFeedback


其中有五个方法,从体感上来说,五种方法的震感不同,其中 vibratelightImpact 感觉上没有太大差别。mediumImpact 相对较弱,heavyImpact 很弱,最后 selectionClick 不知道是我手机问题还是什么,似乎没有震感。

vibrate ≈ lightImpact > mediumImpact > heavyImpact > selectionClick

HapticFeedback 源码中介绍提到,这里的 API 故意设计的比较简洁,只是调用平台的默认行为,并不能达到精确控制系统震动模块目的。也就是说,这里就是简单震动一下,并无法精确控制振幅、震动时长等信息。

【Flutter 小知识】震动反馈 HapticFeedback

对于 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+ 之后引入了新的震动反馈特性,很明显 FlutterHapticFeedback 的方法名称是参照 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 不就行了吗。如果直接一个个塞进去,代码会很长,而且不易管理。

【Flutter 小知识】震动反馈 HapticFeedback

其实这里的数据关系是 字符串和方法(函数)的映射 ,而 方法(函数) 本身也可以作为对象。所以可以使用 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 中各种震动在源码中的使用

首先在 androidfuchsia 中,长按事件会触发 vibrate 震动。iOS 平台一般不会对长按事件进行反馈。

【Flutter 小知识】震动反馈 HapticFeedback

另外,注意一点,在 InkWellTooltip 组件中才会触发 forLongPress ,也就是说 GestureDetector 的长按事件是没有震动反馈的。

【Flutter 小知识】震动反馈 HapticFeedback


lightImpactCupertinoSwitch 组件中被使用,只有在 iOS 平台才会有反馈。

【Flutter 小知识】震动反馈 HapticFeedback

【Flutter 小知识】震动反馈 HapticFeedback


mediumImpactCupertinoSliverRefreshControlCupertinoScrollbar 组件中被使用:

【Flutter 小知识】震动反馈 HapticFeedback


selectionClickCupertinoPickerLongPressDraggableCupertinoContextMenu 中被使用。

【Flutter 小知识】震动反馈 HapticFeedback


最后,heavyImpact 方法没有在框架中被使用。这就是 HapticFeedback 关于震动反馈的一些小知识,本文就到这里,谢谢观看 ~