Flutter手势操作 ---全局坐标与局部坐标的获取

来源:blog.csdn.net 更新时间:2023-05-25 21:55

全局坐标与局部坐标

在GestureDetector中有两个重要属性值globalPositionlocalPosition,两者都是Offset对象
globalPosition就像它的命名表示当前手势触点在**全局坐标系位置对应组件顶点坐标的偏移量(dx,dy)
localPosition则就表示当前手势触点在
对应组件坐标系位置对应组件顶点坐标的偏移量(dx,dy)**。

例如如下代码中,为Container设置了GestureDetector手势监听,在update回调中获取updateDetail对象,在Text中显示globalPosition偏移量。从中获取到的globalPosition和localPosition中dx的值相同,dy却不同。也就是因为Scaffold中设置了AppBar,相对于body他的全局坐标系并非它自身。但若将Scaffold中的AppBar去除,让body撑满整个Scaffold,那么在手势监听中获取到的globalPosition和localPosition偏移量将相同。

需要注意的是对于globalPosition在安卓中还包含了手机状态栏的高度。

通过DragUpdateDetails获取全局坐标和局部坐标

DragUpdateDetails 相当网页前端的Evnet事件,可以获取当前触摸点的全局坐标和局部坐标。

  onPanUpdate: (DragUpdateDetails detail) {   
    //获取当前触摸点的全局坐标
    var globalPosition=detail.globalPosition;
    //获取当前触摸点的局部坐标
    var localPosition=detail.localPosition;
   }
  •  

通过contexnt.findRenderObject()获取全局坐标和局部坐标

当前的页面结构(PainterPage):

  • Scafford

    • body

      • GestureDetector //手势Widget
        • onPanUpdate(detail)
onPanUpdate:(detail){
  final RenderBox Box = context.findRenderObject();   									// 获取的对象为当前页面对象,PainterPage
  Offset localPosition = Box.globalToLocal(detail.globalPosition);			// 转换为局部坐标但实际是全局坐标
  Offset globalPosition = Box.localToGlobal(detail.globalPosition);			// 获得的是全局坐标。
}
 

为甚么通过这样的方式,不能正确获取到局部坐标呢?

原因在于context.findRenderObject(),通过context,获取到的Box,实际上是PaintPage本身。所以,对于Box自身来说全局坐标局部坐标是一致的。

而实际上我们真正想要的是GestureDetector里的坐标系。由前面我们可以知道context.findRenderObject()只能获取context当前所在页面的页面 Widget

为了解决不能获取到GuesterDetector的坐标的问题,我们需要将GuesterDetector封装成一个StatefulWidget或者一个StatelessWidget,即GuesterDetectorWidget。再将GuesterDetectorWidget引入到PainterPage

更详细的说明可以参详字母索引快速定位

onPanUpdate:(detail){
  final RenderBox Box = context.findRenderObject();   									// 获取的对象为当前页面对象,PainterPage
  Offset localPosition = Box.globalToLocal(detail.globalPosition);			// 转换为局部坐标
  Offset globalPosition = Box.localToGlobal(detail.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,
            data-width: double.infinity,
            data-height: double.infinity,
            child: Center(
              child: Text(
                 offsetText,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 25,
                ),
              ),
            ),
          ),
        ),
      ),
    );

可以看到当Scaffold包含和未包含AppBar时,Container两个偏移量输出的差异。

#### 知识地图
  • Flutter的坐标体系是以Widget的左上角为原点,向左为正x轴,以下为正y轴;

  • 全局坐标是整个屏幕的左上角开始计算的

  • 局部坐标,是以当前GestureDetector包裹的Wiget的左上角为原点开始计算的

  •  

参考资料:

Flutter实战之手势操作篇

实现 View 的移动拖拽

flutter 白板工具