Android?Flutter實現(xiàn)任意拖動的控件
前言
使用flutter開發(fā)是需要控件能拖動,比如畫板中的元素,或者工具條,搜索框,每個都單獨去實現(xiàn)拖動還是比較麻煩的,將拖動功能封裝成一個控件,需要的時候直接使用拖動控件作為父控件這樣就方便很多了。
一、如何實現(xiàn)
1、使用GestureDetector響應(yīng)拖動事件
//總位移
var _unlimtedOffset = Offset.zero;
//當(dāng)前位移(有活動區(qū)域限制時,鼠標(biāo)超過邊界后當(dāng)前位移不等于總位移,此時總位移可以確?;氐竭吔鐑?nèi)鼠標(biāo)與控件的相對位置不變)
final _offset = ValueNotifier<Offset>(Offset.zero);
GestureDetector(
child: this.widget.child,
onPanUpdate: (detail) {
//累加拖動距離
_unlimtedOffset += detail.delta;
}
)2、使用Transform變換控件位置
使用translate變換位置即可
//ValueListenableBuilder監(jiān)聽_offset 改變,此處略
Transform.translate(
offset: offset,
child:GestureDetector()//上一步的child:GestureDetector
)3、計算拖動區(qū)域
這一步不是必須的,但是如果需要限制控件活動范圍則需要這一步。
通過GlobalKey獲取控件大小,在GestureDetector的onPanUpdate事件中:
onPanUpdate: (detail) {
//拖動區(qū)域為父控件,去掉則不受限制,但拖出父控件會被遮擋無法點擊。
//獲取父控件大小
RenderBox ? parentRenderBox = _mykey.currentContext
? .findAncestorRenderObjectOfType<RenderObject>() as RenderBox ? ;
final screenSize = parentRenderBox ? .size;
//獲取控件大小
final mySize = _mykey.currentContext ? .size;
final renderBox =
_mykey.currentContext ? .findRenderObject() as RenderBox ? ;
//獲取控件當(dāng)前位置
var originOffset = renderBox ? .localToGlobal(Offset.zero);
if (originOffset != null) {
originOffset = parentRenderBox ? .globalToLocal(originOffset);
}
if (screenSize == null || mySize == null || originOffset == null) {
return;
}
//計算不超出父控件區(qū)域
if (off.dx < -originOffset.dx) {
off = Offset(-originOffset.dx, off.dy);
}
else if (off.dx >
screenSize.width - mySize.width - originOffset.dx) {
off = Offset(
screenSize.width - mySize.width - originOffset.dx,
off.dy,
);
}
if (off.dy < -originOffset.dy) {
off = Offset(off.dx, -originOffset.dy);
}
else if (off.dy >
screenSize.height - mySize.height - originOffset.dy) {
off = Offset(
off.dx,
screenSize.height - mySize.height - originOffset.dy,
);
}
//現(xiàn)在活動區(qū)域為父控件 --end
}二、完整代碼
drag_move_box.dart
import 'package:flutter/material.dart';
/// 可拖動容器
/// 拖動范圍是父控件
class DragMoveBox extends StatefulWidget {
final Widget child;
const DragMoveBox({
super.key,
required this.child,
});
@override
State<DragMoveBox> createState() => _DragMoveBoxState();
}
class _DragMoveBoxState extends State<DragMoveBox> {
final GlobalKey _mykey = GlobalKey();
//當(dāng)前位移(有活動區(qū)域限制時,鼠標(biāo)超過邊界后當(dāng)前位移不等于總位移,此時總位移可以確?;氐竭吔鐑?nèi)鼠標(biāo)與控件的相對位置不變)
final _offset = ValueNotifier<Offset>(Offset.zero);
//總位移
var _unlimtedOffset = Offset.zero;
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _offset,
builder:
//采用transform變換實現(xiàn)拖動
(context, offset, widget) => Transform.translate(
key: _mykey,
offset: offset,
child: GestureDetector(
child: this.widget.child,
onPanUpdate: (detail) {
var off = _unlimtedOffset = _unlimtedOffset + detail.delta;
//拖動區(qū)域為父控件,去掉則不受限制,但拖出父控件會被遮擋無法點擊。
//獲取父控件大小
RenderBox? parentRenderBox = _mykey.currentContext
?.findAncestorRenderObjectOfType<RenderObject>() as RenderBox?;
final screenSize = parentRenderBox?.size;
//獲取控件大小
final mySize = _mykey.currentContext?.size;
final renderBox =
_mykey.currentContext?.findRenderObject() as RenderBox?;
//獲取控件當(dāng)前位置
var originOffset = renderBox?.localToGlobal(Offset.zero);
if (originOffset != null) {
originOffset = parentRenderBox?.globalToLocal(originOffset);
}
if (screenSize == null || mySize == null || originOffset == null) {
return;
}
//計算不超出父控件區(qū)域
if (off.dx < -originOffset.dx) {
off = Offset(-originOffset.dx, off.dy);
} else if (off.dx >
screenSize.width - mySize.width - originOffset.dx) {
off = Offset(
screenSize.width - mySize.width - originOffset.dx,
off.dy,
);
}
if (off.dy < -originOffset.dy) {
off = Offset(off.dx, -originOffset.dy);
} else if (off.dy >
screenSize.height - mySize.height - originOffset.dy) {
off = Offset(
off.dx,
screenSize.height - mySize.height - originOffset.dy,
);
}
//現(xiàn)在活動區(qū)域為父控件 --end
_offset.value = off;
},
),
),
);
}
}三、使用示例
1、基本用法
DragMoveBox(
child:Text("You have pushed the button this many times:") //需要拖動的控件
)2.效果預(yù)覽

總結(jié)
本文提供了一種簡單的拖動控件實現(xiàn),尤其是封裝成容器后使用變得很簡單,主要在于能想到translate變換可以改變位置,在了解通過GlobalKey獲取控件大小以及獲取控件大小的方法,很容易就實現(xiàn)拖動功能了。
以上就是Android Flutter實現(xiàn)任意拖動的控件的詳細(xì)內(nèi)容,更多關(guān)于Android Flutter任意拖動控件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android 中出現(xiàn)java.net.BindException: bind failed: EADDRINUSE 問
這篇文章主要介紹了Android 中出現(xiàn)java.net.BindException: bind failed: EADDRINUSE 問題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04
解決Android-RecyclerView列表倒計時錯亂問題
這篇文章主要介紹了解決Android-RecyclerView列表倒計時錯亂問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
Android開發(fā)之獲取短信驗證碼后按鈕背景變化并且出現(xiàn)倒計時
在開發(fā)是經(jīng)常會遇到獲取短信驗證碼,然后獲取驗證碼后需要等待n秒倒計時,這時是不能再次發(fā)送短信請求的,這是需要一個倒計時程序,本文給大家分享了實現(xiàn)此功能的代碼,需要的朋友參考下2016-01-01
Flutter利用Hero組件實現(xiàn)自定義路徑效果的動畫
本篇介紹了如何利用Hero動畫組件的createRectTween屬性實現(xiàn)自定義路徑效果的動畫。文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-06-06
Android Webview上的ssl warning的處理方式詳解及實例
這篇文章主要介紹了Android Webview上的ssl warning的處理方式詳解及實例的相關(guān)資料,需要的朋友可以參考下2017-02-02

