本文首發於公眾號「劉望舒」
ReactNative入門系列 React Native元件 Flutter基礎系列
前言
移動開發中,使用者互動是一個重要的環節,在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('水平拖動');
},
),
),
),
);
}
}
複製程式碼
只需要在手勢識別這個文字上進行操作,那麼對應的手勢就會被列印出來。
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中移除。
我們向左滑動第一個和第二個item,會出現刪除的動畫,結果如下圖所示。