【Flutter 小知識】震動反饋 HapticFeedback
theme: hydrogen
1. 緣起
這兩天在研究 CupertinoSliverRefreshControl
元件,使用中有個小細節吸引到了我的注意。在下拉達到一定程度時,會有 weng
的一聲震動感。然後翻看原始碼中的具體實現邏輯,在下拉量大於 refreshTriggerPullDistance
時,會觸發 HapticFeedback.mediumImpact();
方法。看到 Feedback
一詞,也就知道這是震動反饋觸發的方法了。
2. HapticFeedback 類的介紹
HapticFeedback
非常簡單,私有構造,且提供五個靜態方法,很明顯是一個工具類。
其中有五個方法,從體感上來說,五種方法的震感不同,其中 vibrate
和 lightImpact
感覺上沒有太大差別。mediumImpact
相對較弱,heavyImpact
很弱,最後 selectionClick
不知道是我手機問題還是什麼,似乎沒有震感。
dart
vibrate ≈ lightImpact > mediumImpact > heavyImpact > selectionClick
HapticFeedback
原始碼中介紹提到,這裡的 API
故意設計的比較簡潔,只是呼叫平臺的預設行為,並不能達到精確控制系統震動模組目的。也就是說,這裡就是簡單震動一下,並無法精確控制振幅、震動時長等資訊。
對於 Android
來說,這五個方法分別對應 HapticFeedbackConstants
中的五個常量:
dart
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
來命名的。
dart
vibrate ---- kSystemSoundID_Vibrate
lightImpact ---- UIImpactFeedbackGenerator-UIImpactFeedbackStyleLight (ios10+)
mediumImpact ---- UIImpactFeedbackGenerator-UIImpactFeedbackStyleMedium (ios10+)
heavyImpact ---- UIImpactFeedbackGenerator-UIImpactFeedbackStyleHeavy (ios10+)
selectionClick ---- UISelectionFeedbackGenerator (ios10+)
3. HapticFeedback 的使用
因為都是靜態方法,所以使用也是非常簡單,呼叫一些即可,比如:
dart
HapticFeedback.vibrate();
下面簡單寫個測試介面,通過點選按鈕來觸發不同的震動反饋。藉此也來說一下,如何優雅地實現這種若干個需要觸發事件的按鈕。可能有人看到這個介面,就想到在 Wrap
放一個個的 ElevatedButton
不就行了嗎。如果直接一個個塞進去,程式碼會很長,而且不易管理。
其實這裡的資料關係是 字串和方法(函式)的對映
,而 方法(函式)
本身也可以作為物件。所以可以使用 Map
來維護資料,為了方便表示函式型別,可以通過 typedef
進行宣告,比如下面的 VoidAsyncFunction
:
```dart
typedef VoidAsyncFunction = Future
final Map
這樣在 Wrap
中,通過 feedbackMap
來遍歷 key
列表,生成 ElevatedButton
即可。其中 feedbackMap[name]
就是代表字串名稱對應的函式物件。
dart
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
並不會立刻震動,向平臺通道傳送訊息是個不確定時長的非同步任務。
dart
static Future<void> vibrate() async {
await SystemChannels.platform.invokeMethod<void>('HapticFeedback.vibrate');
}
如何你需要確切在震動之後才觸發某段邏輯,可以通過 await
來等待非同步任務完成。比如下面連續四次,間隔 500 ms
的震動。需要在前一次震動方法完成,才能開始下次震動。
dart
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
左右。像下拉到一定高度給出震動感,並不是很在意確切的時間。
dart
int tag = DateTime.now().millisecondsSinceEpoch;
await HapticFeedback.vibrate();
int now = DateTime.now().millisecondsSinceEpoch;
print(now-tag);
5. HapticFeedback 中各種震動在原始碼中的使用
首先在 android
和 fuchsia
中,長按事件會觸發 vibrate
震動。iOS
平臺一般不會對長按事件進行反饋。
另外,注意一點,在 InkWell
和 Tooltip
元件中才會觸發 forLongPress
,也就是說 GestureDetector
的長按事件是沒有震動反饋的。
lightImpact
在 CupertinoSwitch
元件中被使用,只有在 iOS
平臺才會有反饋。
mediumImpact
在 CupertinoSliverRefreshControl
和 CupertinoScrollbar
元件中被使用:
selectionClick
在 CupertinoPicker
、LongPressDraggable
、CupertinoContextMenu
中被使用。
最後,heavyImpact
方法沒有在框架中被使用。這就是 HapticFeedback
關於震動反饋的一些小知識,本文就到這裡,謝謝觀看 ~
- 【Flutter&Flame 遊戲 - 貳捌】pinball 原始碼分析 - 遊戲主場景的構成
- 【Flutter&Flame 遊戲 - 貳柒】pinball 原始碼分析 - 角色選擇與玩法面板
- 【Flutter&Flame 遊戲 - 貳陸】pinball 原始碼分析 - 遊戲主頁
- 【Flutter&Flame 遊戲 - 貳伍】pinball 原始碼分析 - 資源載入與 Loading
- 【Flutter&Flame 遊戲 - 貳肆】pinball 原始碼分析 - 專案結構介紹.md
- 【Flutter&Flame 遊戲 - 貳叄】 資源管理與國際化
- 【Flutter&Flame 遊戲 - 貳貳】選單、字型和浮層
- 【Flutter&Flame 遊戲 - 貳壹】視差元件 | ParallaxComponent
- 【Flutter&Flame遊戲 - 貳拾】構件特效 | 其他 EffectControler
- 【Flutter&Flame遊戲 - 拾玖】構件特效 | 瞭解 EffectController 體系
- 【Flutter&Flame遊戲 - 拾捌】構件特效 | ComponentEffect 一族
- 【Flutter&Flame遊戲 - 拾柒】構件特效 | 瞭解 Effect 體系
- 【Flutter&Flame遊戲 - 拾陸】粒子系統 | 粒子的種類
- 【Flutter&Flame遊戲 - 拾伍】粒子系統 | ParticleSystemComponent
- FlutterUnit 桌面分支合併,一套程式碼 - 五端通行
- 【Flutter&Flame遊戲 - 拾肆】碰撞檢測 | 之前程式碼優化
- 【Flutter&Flame遊戲 - 拾叄】碰撞檢測 | CollisionCallbacks
- 【Flutter&Flame遊戲 - 拾貳】探索構件 | 角色管理
- 【Flutter&Flame遊戲 - 拾壹】探索構件 | Component 使用細節
- 【Flutter&Flame遊戲 - 拾】探索構件 | Component 生命週期回撥