Flutter踩坑系列:滑動手勢監(jiān)聽不到

作者:Dreamy

問題描述

在PageView外部使用GestureDetector監(jiān)聽不到左滑手勢。
如下代碼,水平滑動的PageView只監(jiān)聽到了垂直方向的拖拽手勢,卻監(jiān)聽不到水平方向的拖拽手勢。

GestureDetector(
  onVerticalDragStart: (details) => print('onVerticalDragStart'),
  onVerticalDragEnd: (details) => print('onVerticalDragEnd'),
  onHorizontalDragStart: (details) => print('onHorizontalDragStart'),
  onHorizontalDragEnd: (details) => print('onHorizontalDragEnd'),
  child: PageView(
    children: [
      Container(color: Colors.amber),
      Container(color: Colors.blue),
      Container(color: Colors.deepOrangeAccent),
    ],
  ),
);
日志

原因分析

水平手勢已經(jīng)被橫向滑動的PageView監(jiān)聽,所以通過GestureDetector是無法再次監(jiān)聽的,只能使用Listener對原始指針進(jìn)行監(jiān)聽,進(jìn)而達(dá)到監(jiān)聽橫向滑動的效果。下文具體介紹Flutter的手勢監(jiān)聽機(jī)制。

Pointer

Pointer Event指針事件是指最原始的觸摸事件,一次事件包括觸摸到屏幕、在屏幕上移動到離開屏幕。 使用Listener組件來監(jiān)聽指針事件,基本的回調(diào)有onPointerDown、onPointerMove、onPointerUp。

Listener(
  child: Container(
    alignment: Alignment.center,
    color: Colors.blue,
    width: 100,
    height: 100,
  ),
  onPointerDown: (event) => print('onPointerDown'),
  onPointerMove: (event) => print('onPointerMove'),
  onPointerUp: (event) => print('onPointerUp'),
),

Listener的behavior參數(shù)

// How to behave during hit tests.
enum HitTestBehavior {
  // Targets that defer to their children receive events within their bounds
  // only if one of their children is hit by the hit test.
  deferToChild,

  // Opaque targets can be hit by hit tests, causing them to both receive
  // events within their bounds and prevent targets visually behind them from
  // also receiving events.
  opaque,

  // Translucent targets both receive events within their bounds and permit
  // targets visually behind them to also receive events.
  translucent,
}

首先要了解兩個(gè)概念 一個(gè)是渲染層級,還有一個(gè)是Widget的樹結(jié)構(gòu)。當(dāng)手指點(diǎn)擊之后會先通過渲染層級和behavior參數(shù)確定HitTest命中的組件,然后根據(jù)樹結(jié)構(gòu)依次向上傳遞Pointer Event。透明Widget一般HitTest都會失敗,但是behavior參數(shù)可以依照如下說明進(jìn)行設(shè)定。
deferToChild:當(dāng)子節(jié)點(diǎn)widget的HitTest命中測試成功時(shí),該節(jié)點(diǎn)一定會響應(yīng)。
opaque:將當(dāng)前節(jié)點(diǎn)Widget當(dāng)作是不透明的進(jìn)行處理(就算是透明的組件)。
translucent:透傳,正常情況下HitTest只會響應(yīng)渲染層級上面的組件。
IgnorePointer組件 被IgnorePointer組件包裹的子樹及其本身都不會處理PointerEvent事件。
AbsorbPointer組件 被AbsorbPointer組件包裹的子樹不會處理PointerEvent事件,但是AbsorbPointer組件本身會處理。

GestureDetector組件

GestureDetector組件對基本的PointerEvent事件進(jìn)行了語義封裝,通過GestureRecognizer使用Listener將PointerEvent轉(zhuǎn)義成onTap、onDoubleTap、onLongPress、onPanDown、onVeticalDragUpdate等等接口。
Arena: Flutter定義了一個(gè)Arena手勢競技場,對有沖突的手勢進(jìn)行判斷最后選出唯一的一個(gè)獲勝者并處理事件。例如水平和垂直的ListView會根據(jù)橫豎的滑動分量進(jìn)行判斷。
Tip: 手勢沖突只是手勢級別的,而手勢是對原始指針的語義化的識別,所以在遇到復(fù)雜的沖突場景時(shí),都可以通過Listener直接識別原始指針事件來解決沖突。

Notification

NotificationListener組件可以用來監(jiān)聽從子結(jié)構(gòu)傳遞過來的通知,和手勢通知不同,這種Notification可以選擇是否還要往上傳遞。 常用的是ScrollStartNotification、ScrollUpdateNotification等滑動通知,也可以自定義通知dispatch向上分發(fā)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容