跳至主要內容

點擊、拖曳和其他手勢

本文件說明如何在 Flutter 中監聽並回應手勢。手勢的範例包括點擊、拖曳和縮放。

Flutter 中的手勢系統有兩個不同的層。第一層具有原始指標事件,描述指標 (例如觸摸、滑鼠和手寫筆) 在螢幕上的位置和移動。第二層具有手勢,描述由一個或多個指標移動組成的語義動作。

指標

#

指標代表使用者與裝置螢幕互動的原始資料。指標事件有四種類型

PointerDownEvent
指標已在特定位置接觸螢幕。
PointerMoveEvent
指標已從螢幕上的一個位置移動到另一個位置。
PointerUpEvent
指標已停止接觸螢幕。
指標取消事件 (PointerCancelEvent)
來自此指標的輸入不再導向此應用程式。

當指標按下時,框架會對您的應用程式執行命中測試,以確定螢幕上指標接觸位置存在哪個 Widget。然後,指標按下事件(以及該指標的後續事件)會被派發到命中測試找到的最內層 Widget。從那裡,事件會向上冒泡至樹狀結構,並被派發到從最內層 Widget 到樹狀結構根目錄路徑上的所有 Widget。沒有任何機制可以取消或停止指標事件的進一步派發。

若要直接從 Widget 層監聽指標事件,請使用 Listener Widget。然而,一般來說,請考慮改用手勢(如下所述)。

手勢

#

手勢代表從多個單獨的指標事件(甚至可能是多個單獨的指標)中識別出的語義動作(例如,點擊、拖曳和縮放)。手勢可以派發多個事件,對應於手勢的生命週期(例如,拖曳開始、拖曳更新和拖曳結束)。

點擊

onTapDown
一個可能導致點擊的指標已在特定位置接觸螢幕。
onTapUp
觸發點擊的指標已停止在特定位置接觸螢幕。
onTap
先前觸發 onTapDown 的指標也觸發了 onTapUp,最終導致點擊。
onTapCancel
先前觸發 onTapDown 的指標不會導致點擊。

雙擊

onDoubleTap
使用者已在同一個位置快速連續點擊螢幕兩次。

長按

onLongPress
一個指標在同一個位置與螢幕保持接觸一段長時間。

垂直拖曳

onVerticalDragStart
一個指標已接觸螢幕,並且可能會開始垂直移動。
onVerticalDragUpdate
一個與螢幕接觸並垂直移動的指標已在垂直方向上移動。
onVerticalDragEnd
一個先前與螢幕接觸並垂直移動的指標不再與螢幕接觸,並且在停止接觸螢幕時以特定速度移動。

水平拖曳

onHorizontalDragStart
一個指標已接觸螢幕,並且可能會開始水平移動。
onHorizontalDragUpdate
一個與螢幕接觸並水平移動的指標已在水平方向上移動。
onHorizontalDragEnd
一個先前與螢幕接觸並水平移動的指標不再與螢幕接觸,並且在停止接觸螢幕時以特定速度移動。

平移

onPanStart
一個指標已接觸螢幕,並且可能會開始水平或垂直移動。如果設定了 onHorizontalDragStartonVerticalDragStart,則此回呼會崩潰。
onPanUpdate
一個與螢幕接觸並在垂直或水平方向上移動的指標。如果設定了 onHorizontalDragUpdateonVerticalDragUpdate,則此回呼會崩潰。
onPanEnd
一個先前與螢幕接觸的指標不再與螢幕接觸,並且在停止接觸螢幕時以特定速度移動。如果設定了 onHorizontalDragEndonVerticalDragEnd,則此回呼會崩潰。

將手勢偵測新增至 Widget

#

若要從 Widget 層監聽手勢,請使用 GestureDetector

如果您使用的是 Material Components,則其中許多 Widget 已會回應點擊或手勢。例如,IconButtonTextButton 會回應按下(點擊),而 ListView 會回應滑動以觸發捲動。如果您未使用這些 Widget,但想要點擊時出現「墨水濺射」效果,則可以使用 InkWell

手勢消除歧義

#

在螢幕上的特定位置,可能有多個手勢偵測器。例如

  • 一個 ListTile 有一個回應整個 ListTile 的點擊辨識器,以及一個巢狀的點擊辨識器在尾端圖示按鈕周圍。尾端圖示的螢幕矩形現在被兩個手勢辨識器覆蓋,如果最終是點擊,它們需要協商由誰處理手勢。
  • 單個 GestureDetector 涵蓋一個螢幕區域,該螢幕區域配置為處理多個手勢,例如長按和點擊。當使用者觸摸螢幕的該部分時,tap 辨識器現在必須與 long press 辨識器協商。根據該指標接下來發生的情況,兩個辨識器中的一個會接收到手勢,或者如果使用者執行既不是點擊也不是長按的操作,則兩個辨識器都不會接收到手勢。

所有這些手勢偵測器都會監聽流經的指標事件串流,並嘗試辨識特定的手勢。GestureDetector Widget 會根據其哪些回呼不是 null 來決定要嘗試辨識哪些手勢。

當螢幕上給定指標有多個手勢辨識器時,框架會讓每個辨識器加入手勢競技場來消除使用者意圖的歧義。手勢競技場使用以下規則確定哪個手勢獲勝

  • 任何時候,辨識器都可以自行淘汰並離開競技場。如果競技場中只剩一個辨識器,則該辨識器獲勝。

  • 任何時候,辨識器都可以宣告自己獲勝,導致所有剩餘的辨識器失敗。

例如,在消除水平和垂直拖曳的歧義時,當它們接收到指標按下事件時,兩個辨識器都會進入競技場。辨識器會觀察指標移動事件。如果使用者將指標水平移動超過一定數量的邏輯像素,則水平辨識器會宣告勝利,並且該手勢被解釋為水平拖曳。同樣地,如果使用者垂直移動超過一定數量的邏輯像素,則垂直辨識器會宣告自己獲勝。

當只有水平(或垂直)拖曳辨識器時,手勢競技場會很有幫助。在這種情況下,競技場中只有一個辨識器,並且會立即辨識水平拖曳,這表示水平移動的第一個像素可以被視為拖曳,而使用者不需要等待進一步的手勢消除歧義。