最近項目中有個需求,widget要能夠移動宝惰、旋轉、點擊商膊,代碼實現如下饶囚。
class GestureDetectorDemo extends StatefulWidget {
@override
_GestureDetectorDemoState createState() => _GestureDetectorDemoState();
}
class _GestureDetectorDemoState extends State<GestureDetectorDemo> {
bool _show = true;
@override
Widget build(BuildContext context) {
// final control = TransformationController();
// control.
return _buildPanel();
}
//靜止狀態(tài)下的offset
Offset _idleOffset = Offset(0, 0);
//本次移動的offset
Offset _moveOffset = Offset(0, 0);
//最后一次down事件的offset
Offset _lastStartOffset = Offset(0, 0);
double _previousScale = 1;
double _scale = 1;
double _itemWidth = 150;
double _itemHeight = 150;
Widget _buildPanel() {
return Transform.scale(
scale: _scale,
alignment: Alignment(0, 0),
child: Transform.translate(
offset: _moveOffset,
child: GestureDetector(
onScaleStart: _onScaleStart,
onScaleUpdate: _onScaleUpdate,
onScaleEnd: _onScaleEnd,
onTap: _onTap,
child: Container(
color: Colors.red,
width: _itemWidth,
height: _itemHeight,
)),
),
);
}
void _onTap(){
print('_onTap');
}
void _onScaleStart(ScaleStartDetails details) {
setState(() {
_lastStartOffset = details.focalPoint;
print('onScaleStart $_lastStartOffset');
Offset local = details.localFocalPoint;
});
}
void _onScaleUpdate(ScaleUpdateDetails details) {
setState(() {
// 縮放
_scale = _previousScale * details.scale;
// 移動
_moveOffset =
(details.focalPoint - _lastStartOffset + _idleOffset) / _scale;
// 控制移動邊界
double left =
(_itemWidth * _scale - _itemWidth) / 2.0 / _scale - _itemWidth / 2;
double top = ((_itemHeight * _scale - _itemHeight) / 2.0) / _scale -
_itemHeight / 2;
_moveOffset = Offset(max(left, _moveOffset.dx), max(top, _moveOffset.dy));
double right = (SizeFit.screenWidth -
_itemWidth -
(_itemWidth * _scale - _itemWidth) / 2.0) /
_scale +
_itemWidth / 2;
double bottom = (SizeFit.screenHeight -
_itemHeight -
(_itemHeight * _scale - _itemHeight) / 2.0 -
LayoutUtil.getAppBarHeight(context)) /
_scale +
_itemHeight / 4;
_moveOffset =
Offset(min(right, _moveOffset.dx), min(bottom, _moveOffset.dy));
});
}
void _onScaleEnd(ScaleEndDetails details) {
print('onScaleEnd');
setState(() {
_idleOffset = _moveOffset * _scale;
_previousScale = _scale;
});
}
}
基本功能都使用沒什么問題帕翻,但是測試中發(fā)現在進行縮放的時候總是延遲,手勢執(zhí)行一定時間后widget才開始縮放萝风。
造成這種情況的原因應該是手勢沖突嘀掸,解決的思路是用Listener處理tap事件,GestureDetector只處理縮放和移動事件规惰。代碼如下
class GestureDetectorDemo extends StatefulWidget {
@override
_GestureDetectorDemoState createState() => _GestureDetectorDemoState();
}
class _GestureDetectorDemoState extends State<GestureDetectorDemo> {
bool _show = true;
@override
Widget build(BuildContext context) {
// final control = TransformationController();
// control.
return _buildPanel();
}
//靜止狀態(tài)下的offset
Offset _idleOffset = Offset(0, 0);
//本次移動的offset
Offset _moveOffset = Offset(0, 0);
//最后一次down事件的offset
Offset _lastStartOffset = Offset(0, 0);
double _previousScale = 1;
double _scale = 1;
double _itemWidth = 150;
double _itemHeight = 150;
Duration _tapDownTimeStamp;
Widget _buildPanel() {
return Transform.scale(
scale: _scale,
alignment: Alignment(0, 0),
child: Transform.translate(
offset: _moveOffset,
child: Listener(
onPointerDown: (PointerDownEvent event) {
// 記錄點擊時間
_tapDownTimeStamp = event.timeStamp;
},
onPointerUp: (PointerUpEvent event) {
// 手指抬起時睬塌,計算時間差,100ms以內算點擊事件
int interval = event.timeStamp.inMilliseconds -
_tapDownTimeStamp.inMilliseconds;
if (interval <= 100) {
// 處理tap事件
_onTap();
}
},
child: GestureDetector(
onScaleStart: _onScaleStart,
onScaleUpdate: _onScaleUpdate,
onScaleEnd: _onScaleEnd,
child: Container(
color: Colors.red,
width: _itemWidth,
height: _itemHeight,
)),
),
),
);
}
void _onTap() {
print('_onTap');
}
void _onScaleStart(ScaleStartDetails details) {
setState(() {
_lastStartOffset = details.focalPoint;
print('onScaleStart $_lastStartOffset');
Offset local = details.localFocalPoint;
});
}
void _onScaleUpdate(ScaleUpdateDetails details) {
setState(() {
// 縮放
_scale = _previousScale * details.scale;
// 移動
_moveOffset =
(details.focalPoint - _lastStartOffset + _idleOffset) / _scale;
// 控制移動邊界
double left =
(_itemWidth * _scale - _itemWidth) / 2.0 / _scale - _itemWidth / 2;
double top = ((_itemHeight * _scale - _itemHeight) / 2.0) / _scale -
_itemHeight / 2;
_moveOffset = Offset(max(left, _moveOffset.dx), max(top, _moveOffset.dy));
double right = (SizeFit.screenWidth -
_itemWidth -
(_itemWidth * _scale - _itemWidth) / 2.0) /
_scale +
_itemWidth / 2;
double bottom = (SizeFit.screenHeight -
_itemHeight -
(_itemHeight * _scale - _itemHeight) / 2.0 -
LayoutUtil.getAppBarHeight(context)) /
_scale +
_itemHeight / 4;
_moveOffset =
Offset(min(right, _moveOffset.dx), min(bottom, _moveOffset.dy));
});
}
void _onScaleEnd(ScaleEndDetails details) {
print('onScaleEnd');
setState(() {
_idleOffset = _moveOffset * _scale;
_previousScale = _scale;
});
}
}