這是第二篇番外了,第一篇是關於動畫翻轉效果實現而此次要說的是運用手勢操做實現相似於Drawer抽屜拖拽功能。Drawer是默認所有隱藏在屏幕外,在屏幕邊緣作手勢操做使其滑出或者經過點擊實現滑出。而我但願實現默認帶偏移量和多方向側拉效果功能,在默認狀況下可以展現側邊組件部份內容經過拖拽展現組件總體內容,可能描述上很差理解就直接上圖吧😂。git
PS:爲何會有這樣的需求,我的認爲默認狀況下可以展現出組件部份內容可讓用戶更直觀地知道側邊可操做,感官上交互性更強😆。(例如豆瓣電影底部影評列表上拉展現)以上純屬我的對於交互設計理解╮( ̄▽ ̄)╭github
實現拖拽功能確定離不開手勢操做和位移這兩點,而後結合這兩點組合操做實現抽屜拖拽滑出的功能組件。segmentfault
本次實現方案中使用到的組件包含:Transform 、RawGestureDetector 、 AnimationController 、 Animation。Transform、AnimationController、Animatio在以前實戰篇中都有介紹過而RawGestureDetector是第一次據說。RawGestureDetector也是實現手勢監聽組件和GestureDetector類似但比起GestureDetector使用上更爲複雜。markdown
簡要介紹一下二者不一樣點:post
因此在方案中徹底可使用GestureDetector代替RawGestureDetector更簡單,純粹是由於沒有用過RawGestureDetector因此嘗試一下😄學習
Transform.translate(
offset: Offset(offsetX, offsetY),
child: RawGestureDetector(
gestures: {
DrawerPanGestureRecognizer:
GestureRecognizerFactoryWithHandlers<DrawerPanGestureRecognizer>(
() => DrawerPanGestureRecognizer(),
(DrawerPanGestureRecognizer instance) {
instance
..onUpdate = (DragUpdateDetails details) {
// 省略處理邏輯,獲取實時手勢座標點根據拖拽方向更新Transform的offsetX或offsetY
}
}
..onEnd = (DragEndDetails details) {
// 省略處理邏輯 手勢擡起操做根據位移距離判斷組件展開或是縮回動畫
}
};
},
),
},
child: Container(
width: width,
child: widget.child,
),
),
);
複製代碼
動畫分爲展開動畫和縮回動畫。經過手勢位移量大小決定執行對應動畫效果,一樣是經過監聽動畫執行過程當中animation.value數值變化改變offsetX或offsetY的大小實現組件移動最終位移到最終點。動畫
/// 復原動畫
_setCallBackAnimation() {
double offset;
switch (widget.direction) { //根據方向設置起始偏移值
case DragDirection.top:
case DragDirection.bottom:
offset = offsetY;
break;
case DragDirection.left:
case DragDirection.right:
offset = offsetX;
break;
}
print("dragdemo _setCallBackAnimation begin: $offset, end: $originOffset");
_animation = Tween<double>(begin: offset, end: originOffset).animate(
CurvedAnimation(
parent: _callbackAnimationController, curve: Curves.easeOut))
..addListener(() {
print("dragdemo _setCallBackAnimation ${_animation.value}");
setState(() {
switch (widget.direction) {
case DragDirection.top:
case DragDirection.bottom:
offsetY = _animation.value;
break;
case DragDirection.left:
case DragDirection.right:
offsetX = _animation.value;
break;
}
});
});
}
/// 展開動畫
_setToMaxAnimation() {
double offset;
switch (widget.direction) {//根據方向設置起始偏移值
case DragDirection.top:
case DragDirection.bottom:
offset = offsetY;
break;
case DragDirection.left:
case DragDirection.right:
offset = offsetX;
break;
}
print("dragdemo _setToMaxAnimation begin: $offset, end: $maxOffset");
_animation = Tween<double>(begin: offset, end: maxOffset).animate(
CurvedAnimation(
parent: _toMaxAnimationController, curve: Curves.easeOutQuart))
..addListener(() {
print("dragdemo _setToMaxAnimation ${_animation.value}");
setState(() {
switch (widget.direction) {
case DragDirection.top:
case DragDirection.bottom:
offsetY = _animation.value;
break;
case DragDirection.left:
case DragDirection.right:
offsetX = _animation.value;
break;
}
});
});
}
複製代碼
GestureDragDrawer(
{this.child, //拖拽組件要展現的子內容
this.childSize = 0, //子內容的大小
this.originOffset = 0, //預設偏移量
this.parentWidth = 0, //父級組件的寬度 當拖拽組件在bottom和right時須要用到
this.parentHeight = 0,//父級組件的高度
this.direction = DragDirection.left});
.....
_initValue() {
width = widget.childSize.abs();
minOffset = -width / 2;
midOffset = -width / 3;
maxOffset = 0;
/// 底部和右邊的偏移量須要特殊計算初始值(右邊和底部的值偏移量 = 父組件的寬或高 - 初始預設偏移量)
switch (widget.direction) {
case DragDirection.bottom:
originOffset = widget.parentHeight - widget.originOffset;
maxOffset = widget.parentHeight - width;
midOffset = maxOffset + width / 3;
minOffset = maxOffset + width / 2;
break;
case DragDirection.right:
originOffset = widget.parentWidth - widget.originOffset;
maxOffset = widget.parentWidth - width;
midOffset = maxOffset + width / 3;
minOffset = maxOffset + width / 2;
break;
default:
originOffset = -width + widget.originOffset;
break;
}
}
複製代碼
最終實現效果以下(demo若是存在問題能夠告訴我哈~這樣我才能進步)this
考慮之後會寫更多自定義組件或Demo的可能性,因此Flutter番外篇組合成一個項目庫方便本身學習和總結。我也將第一篇番外Flip翻轉效果完整代碼添加到該項目中了但願小夥伴多多點贊支持一下。spa
最後的最後歡迎你們下載我本身開發的應用時鐘軟件,若是手頭有閒置Android手機能夠下載安裝平常使用。