Flutter 基礎(八)手勢相關 Widget:GestureDetector 和 Dismissible

NiZerin發表於2019-06-30

Flutter基礎(八)手勢相關Widget:GestureDetector和Dismissible

前言

移動開發中,使用者互動是一個重要的環節,在Android中的觸控、點選、滑動等事件處理都提供了相關的Api,在Flutter中也是一樣的,是由Widget來實現的。
Flutter中的手勢系統有兩個獨立的層。第一層是原始指標事件(pointer events),它描述了螢幕上指標,比如觸控、滑鼠、觸控筆的位置和移動。 第二層是手勢,由一個或多個指標移動組成的動作會被識別為不同的手勢。

1.指標事件

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

  • PointerDownEvent:指標接觸到螢幕的特定位置。
  • PointerMoveEvent: 指標已從螢幕上的一個位置移動到另一個位置。
  • PointerUpEvent: 指標已停止接觸螢幕。
  • PointerCancelEvent:此指標的輸入不再指向此應用,通俗來講就是事件取消。

在指標按下時,Flutter框架會對當前應用程式執行命中測試,以確定指標與螢幕接觸的位置存在哪個Widget上,然後將PointerDownEvent事件(以及該指標的後續事件)排程到命中測試找到的最內部的Widget,事件的分配路徑為:從最裡面的Widget到樹的根路徑上的所有Widget。

2.手勢

手勢表示由一個或多個指標移動組成的動作。主要有以下幾種:

點選
onTapDown:指標已經在特定位置與螢幕接觸。
onTapUp:指標停止在特定位置與螢幕接觸。
onTap :點選事件觸發。
onTapCancel: 先前指標觸發的onTapDown不會再觸發點選事件。

雙擊
onDoubleTap:使用者快速連續兩次在同一位置輕敲螢幕。
長按
onLongPress:指標在相同位置長時間保持與螢幕接觸。
垂直拖動
onVerticalDragStart:指標已經與螢幕接觸並可能開始垂直移動。
onVerticalDragUpdate 指標與螢幕接觸並已沿垂直方向移動。
onVerticalDragEnd 先前與螢幕接觸並垂直移動的指標不再與螢幕接觸,並且在停止接觸螢幕時以特定速度移動。
水平拖動
onHorizontalDragStart:指標已經接觸到螢幕並可能開始水平移動
onHorizontalDragUpdate:指標與螢幕接觸並已沿水平方向移動
onHorizontalDragEnd:先前與螢幕接觸並水平移動的指標不再與螢幕接觸,並在停止接觸螢幕時以特定速度移動。

如何對這些手勢進行檢測呢?可以使用GestureDetector。

3.使用GestureDetector

要想檢測單擊、雙擊、垂直拖動等手勢,只要用GestureDetector巢狀要檢測手勢Widget並實現想要監聽的手勢的方法就行。


import 'package:flutter/material.dart';

void main() => runApp(GestureDetectorWidget());

class GestureDetectorWidget extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return MaterialApp(
 title: "Flutter",
 home: Scaffold(
 appBar: AppBar(
 title: Text("GestureDetector示例"),
 ),
 body: Center(
 child: GestureDetector(
 child: Text('手勢識別'),
 onTap: () {
 print('點選');
 },
 onDoubleTap: () {
 print('雙擊');
 },
 onLongPress: () {
 print('長按');
 },
 onHorizontalDragStart: (DragStartDetails details) {
 print('水平拖動');
 },
 ),
 ),
 ),
 );
 }
}

只需要在手勢識別這個文字上進行操作,那麼對應的手勢就會被列印出來。
V5ddxA.png

4.使用Dismissible

滑動刪除這個操作很常見,比如在一個列表中,我們向左滑動,就會直接刪除一個條目或者給出刪除提示選項。Flutter提供了Dismissible來幫助我們實現滑動刪除。


import 'package:flutter/material.dart';

void main() => runApp(DismissibleWidget(
 items: new List<String>.generate(300, (i) => "第$i行"),
 ));

class DismissibleWidget extends StatelessWidget {
 final List<String> items;

 DismissibleWidget({@required this.items});

 @override
 Widget build(BuildContext context) {
 return MaterialApp(
 title: 'Flutter',
 home: Scaffold(
 appBar: AppBar(
 title: Text('Dismissible示例'),
 ),
 body: ListView.builder(
 itemCount: items.length,
 itemBuilder: (context, index) {
 final item = items[index];
 return Dismissible(
 key: Key(item),
 onDismissed: (direction) {
 items.removeAt(index);
 print(index);
 },
 child: ListTile(
 leading: Icon(Icons.access_time),
 title: Text('${items[index]}'),
 ),
 );
 },
 ),
 ),
 );
 }
}

這個例子和ListView的例子類似,主要的變化就是用Dismissible來巢狀ListTile。當執行刪除操作時,ListView中的onDismissed方法會被回撥,我們可以直接在onDismissed方法中將被刪除的item從List中移除。
V5dBrt.png

我們向左滑動第一個和第二個item,會出現刪除的動畫,結果如下圖所示。
V5wpdK.png


By: Laravel-China 寧澤林
MyBlog: nizer.in

相關文章