前言
日常開發應用中少不了互動功能,而互動就包括了點選觸碰。比如點選一個按鈕對操作做出響應又或者是拖拽列表做下拉重新整理操作等一些常規互動的實現離不開手勢功能。那麼在Flutter中是如何實現通過手勢操作實現互動操作的,下面就來介紹一下Flutter手勢功能。
手勢(GestureDetector)
Flutter通過使用GestureDetector元件使得其他Widget支援手勢功能。手勢可以響應多種操作,主要分為以下幾類:Tap、Double tap、Long press、Vertical drag、Horizontal drag、Pan。同時可以通過動作分為點選、雙擊、長按、拖拽、縮放等。
點選(Tap)& 雙擊(DoubleTap)& 長按(LongPress)
通用的操作方法,例如點選、雙擊、長按功能,只需要編寫Function就能實現功能。當然在這些操作過程中也具備中間過程的狀態監聽,例如點選按下時,抬起時,取消點選等。
GestureDetector(
onTap: (){
showSnack(context, "OnTap");
},
onDoubleTap: (){
showSnack(context, "onDoubleTap");
},
onLongPress: (){
showSnack(context, "onLongPress");
},
child: Transform.translate(
offset: offset,
child: Text(
"Transform",
style: TextStyle(fontSize: 25),
),
),
);
複製程式碼
拖拽(Drag)
GestureDetector為拖拽功能提供水平方向拖拽(Horizontal drag)、垂直方向拖拽(Vertical drag)、同時支援兩個方向拖拽(Pan)。
水平和垂直兩個拖拽監聽只能獲取對應方向偏移量變化,對應的垂直方向的數值返回始終為0,若在開發需求中若同時需要監聽兩個方向則推薦使用綜合拖拽監聽回撥Pan同時監聽水平和垂直方向的偏移量的變化。在監聽中Down、Start、Update三個過程可以監聽到Details物件,從而知道當前手勢定位值對於元件頂點位置的偏移量。
偏移量
在GestureDetector中兩個重要屬性值globalPosition和localPosition,兩者都是Offset物件。globalPosition就像它的命名錶示當前手勢觸點在全域性座標系位置與對應元件頂點座標的偏移量(dx,dy),而localPosition則就表示當前手勢觸點在對應元件座標系位置與對應元件頂點座標的偏移量(dx,dy)也就是說如果當前手勢位置在元件頂點座標那麼dx和dy都為0。
例如如下程式碼中,為Container設定了GestureDetector手勢監聽,在update回撥中獲取updateDetail物件,在Text中顯示globalPosition偏移量。從中獲取到的globalPosition和localPosition中dx都是相同值,dy卻不同。也就是因為Scaffold中設定了AppBar,相對於body他的全域性座標系並非它自身。但若將Scaffold中的AppBar去除,讓body撐滿整個Scaffold,那麼在手勢監聽中獲取到的globalPosition和localPosition將相同。
需要注意的是對於globalPosition在安卓中還包含了手機狀態列的高度。
MaterialApp(
theme: AppTheme.themes[store.state.appThemeState.themeType],
home: Scaffold(
appBar: AppBar(),
body: GestureDetector(
onPanStart: (detail) {
showLog(detail.runtimeType, detail.localPosition,
detail.globalPosition);
},
onPanUpdate: (detail) {
showLog(detail.runtimeType, detail.localPosition,
detail.globalPosition);
setState(() {
offsetText = "globalPosition: ${Offset(detail.globalPosition.dx, detail.globalPosition.dy).toString()} \n"
"localPosition: ${Offset(detail.localPosition.dx, detail.localPosition.dy).toString()}";
});
},
onPanEnd: (detail) {
setState(() {
offsetText = "end";
});
},
child: Container(
color: Colors.red,
width: double.infinity,
height: double.infinity,
child: Center(
child: Text(
offsetText,
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
),
),
),
);
複製程式碼
可以看到當Scaffold包含和未包含AppBar時,Container兩個偏移量輸出的差異。
縮放(Scale)
縮放操作則需要兩個指標物件配合才能發揮作用。當在螢幕上只有一個指標物件時也就是一個觸點時,對應的縮放比為1和角度為0;當螢幕上出現兩個指標物件時,根據兩個初始位置作為基準點,也就是起始位置初始化縮放比為1和角度為0,通過手勢變化得出兩點的距離位置和夾角大小來計算縮放比和角度值。
GestureDetector(
onScaleStart: (detail) {
print(detail);
setState(() {
offsetText = "focalPoint: ${detail.focalPoint.toString()} \n"
"localFocalPoint: ${detail.localFocalPoint.toString()}";
});
},
onScaleUpdate: (details) {
print(details);
Vector.Matrix3 matrix3 = Vector.Matrix3.zero();
setState(() {
offsetText = "focalPoint: ${details.focalPoint.toString()} \n"
"localFocalPoint: ${details.localFocalPoint.toString()}";
scaleUpdateDetails = details;
});
},
onScaleEnd: (details) {
print(details);
setState(() {
scaleUpdateDetails = ScaleUpdateDetails();
});
},
child: Container(
color: Colors.red,
width: double.infinity,
height: double.infinity,
child: Center(
// 元件的動畫效果
child: Transform(
transform: Matrix4.rotationZ(scaleUpdateDetails.rotation)
.scaled(
scaleUpdateDetails.scale),
child: Text(
offsetText,
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
),
),
),
複製程式碼
在onScaleUpdate方法中獲取ScaleUpdateDetails物件,其包含了兩觸點實現的縮放值scale、角度值rotation、偏移量focalPoint資訊。通過上述程式碼並可實現手勢再結合上Transform的元件動效,關於Transform做過多介紹,主要為實現元件動畫效果的元件之後若有時間再做學習。
結尾
這篇只能算是手勢功能基礎篇,還有一些手勢高階功能實現未探究類似於Android中的手勢衝突等一些功能性自定義開發,暫且先熟悉手勢基本功能之後若有對這方面開發需求再做更深層次學習和研究。