Dab*_*bel 1 transform gesturedetector flutter
背景
尝试设置一个简单的图像编辑器,允许用户通过手势缩放和移动图像。
GestureDetector通过,Transform.scale和可以很好地进行缩放和移动Transform.rotate。
问题
缩放后,用户仍然可以缩放已经缩放的图像。
但是:这GestureDetector不会改变执行命中测试的区域。
问题:用户只能使用原始的 hitbox 来操作图像。无法通过在扩展的外部形状上使用两指捏合手势来缩放图像。
图片
第一张图片演示了基本设置。
第二张图片演示了使用手势的结果。它显示了小的、未改变的内部碰撞箱。以及由此产生的缩放和旋转形状。
填充的框是碰撞框。外部矩形显示缩放后的图像。
期望的行为
在缩放后的外部形状上使用两指捏合手势应该允许进一步操作对象。
相反,内部打击盒可以单独使用。但用户期望使用缩放后的外部形状来进一步缩放和移动对象。
代码
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: GestureTest(),
);
}
}
class DrawContainer {
Color color;
Offset offset;
double scale;
double angle;
late double baseScaleFactor;
DrawContainer(this.color, this.offset, this.scale, this.angle) {
baseScaleFactor = scale;
}
onScaleStart() => baseScaleFactor = scale;
onScaleUpdate(double scaleNew) =>
scale = (baseScaleFactor * scaleNew).clamp(0.5, 5);
}
class GestureTest extends StatefulWidget {
const GestureTest({Key? key}) : super(key: key);
@override
// ignore: library_private_types_in_public_api
_GestureTestState createState() => _GestureTestState();
}
class _GestureTestState extends State<GestureTest> {
bool doRedraw = false;
final List<DrawContainer> containers = [
DrawContainer(Colors.red, const Offset(50, 50), 1.0, 0.0),
DrawContainer(Colors.yellow, const Offset(100, 100), 1.0, 0.0),
DrawContainer(Colors.green, const Offset(150, 150), 1.0, 0.0),
];
void onGestureStart(DrawContainer e) => e.onScaleStart();
onGestureUpdate(DrawContainer e, ScaleUpdateDetails d) {
e.offset = e.offset + d.focalPointDelta;
if (d.rotation != 0.0) e.angle = d.rotation;
if (d.scale != 1.0) e.onScaleUpdate(d.scale);
setState(() => doRedraw = !doRedraw); // redraw
}
void rebuildAllChildren(BuildContext context) {
void rebuild(Element el) {
el.markNeedsBuild();
el.visitChildren(rebuild);
}
(context as Element).visitChildren(rebuild);
}
@override
Widget build(BuildContext context) {
rebuildAllChildren(context);
return SafeArea(
child: Scaffold(
body: Stack(
fit: StackFit.expand,
children: [
doRedraw ? const SizedBox.shrink() : const SizedBox.shrink(),
...containers.map((e) {
return Positioned(
top: e.offset.dy,
left: e.offset.dx,
child: Container(
color: e.color,
child: GestureDetector(
onScaleStart: (details) {
if (details.pointerCount == 2) {
onGestureStart(e);
}
},
onScaleUpdate: (details) => onGestureUpdate(e, details),
child: Transform.rotate(
angle: e.angle,
child: Transform.scale(
scale: e.scale,
child: Container(
decoration: BoxDecoration(
border: Border.all(color: e.color)),
width: 100,
height: 100),
// Text(e.label, style: const TextStyle(fontSize: 40)),
))),
// ),
));
}).toList(),
],
),
));
}
}
Run Code Online (Sandbox Code Playgroud)
下面是一个通用的工作示例,其中碰撞框的大小与缩放后的小部件的大小相匹配。
基本结构如下:
SizedBox (infinite size) # may not be needed
- Stack
- GestureDetector for each Widget
- Stack
- Positioned, Transform
- Widget
Run Code Online (Sandbox Code Playgroud)
SizedBox (infinite size) # may not be needed
- Stack
- GestureDetector for each Widget
- Stack
- Positioned, Transform
- Widget
Run Code Online (Sandbox Code Playgroud)
这个测试用例很有帮助:/sf/answers/4785231321/
| 归档时间: |
|
| 查看次数: |
1156 次 |
| 最近记录: |