阅读 601

Flutter实战之手势基础篇

前言

日常开发应用中少不了交互功能,而交互就包括了点击触碰。比如点击一个按钮对操作做出响应又或者是拖拽列表做下拉刷新操作等一些常规交互的实现离不开手势功能。那么在Flutter中是如何实现通过手势操作实现交互操作的,下面就来介绍一下Flutter手势功能。

手势(GestureDetector)

Flutter通过使用GestureDetector组件使得其他Widget支持手势功能。手势可以响应多种操作,主要分为以下几类:Tap、Double tap、Long press、Vertical drag、Horizontal drag、Pan。同时可以通过动作分为点击、双击、长按、拖拽、缩放等。

点击(Tap)& 双击(DoubleTap)& 长按(LongPress)

通用的操作方法,例如点击、双击、长按功能,只需要编写Function就能实现功能。当然在这些操作过程中也具备中间过程的状态监听,例如点击按下时,抬起时,取消点击等。

GestureDetector(
  onTap: (){
    showSnack(context, "OnTap");
  },
  onDoubleTap: (){
    showSnack(context, "onDoubleTap");
  },
  onLongPress: (){
    showSnack(context, "onLongPress");
  },
  child: Transform.translate(
    offset: offset,
    child: Text(
      "Transform",
      style: TextStyle(fontSize: 25),
    ),
  ),
);
复制代码

拖拽(Drag)

GestureDetector为拖拽功能提供水平方向拖拽(Horizontal drag)、垂直方向拖拽(Vertical drag)、同时支持两个方向拖拽(Pan)。

水平和垂直两个拖拽监听只能获取对应方向偏移量变化,对应的垂直方向的数值返回始终为0,若在开发需求中若同时需要监听两个方向则推荐使用综合拖拽监听回调Pan同时监听水平和垂直方向的偏移量的变化。

在监听中Down、Start、Update三个过程可以监听到Details对象,从而知道当前手势定位值对于组件顶点位置的偏移量。

偏移量

在GestureDetector中两个重要属性值globalPosition和localPosition,两者都是Offset对象。globalPosition就像它的命名表示当前手势触点在全局坐标系位置与对应组件顶点坐标的偏移量(dx,dy),而localPosition则就表示当前手势触点在对应组件坐标系位置与对应组件顶点坐标的偏移量(dx,dy)也就是说如果当前手势位置在组件顶点坐标那么dx和dy都为0。

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

需要注意的是对于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,
            width: double.infinity,
            height: double.infinity,
            child: Center(
              child: Text(
                 offsetText,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 25,
                ),
              ),
            ),
          ),
        ),
      ),
    );
复制代码

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

缩放(Scale)

缩放操作则需要两个指针对象配合才能发挥作用。当在屏幕上只有一个指针对象时也就是一个触点时,对应的缩放比为1和角度为0;当屏幕上出现两个指针对象时,根据两个初始位置作为基准点,也就是起始位置初始化缩放比为1和角度为0,通过手势变化得出两点的距离位置和夹角大小来计算缩放比和角度值。

GestureDetector(
          onScaleStart: (detail) {
            print(detail);
            setState(() {
              offsetText = "focalPoint: ${detail.focalPoint.toString()} \n"
                  "localFocalPoint: ${detail.localFocalPoint.toString()}";
            });
          },
          onScaleUpdate: (details) {
            print(details);
            Vector.Matrix3 matrix3 = Vector.Matrix3.zero();
            setState(() {
              offsetText = "focalPoint: ${details.focalPoint.toString()} \n"
                  "localFocalPoint: ${details.localFocalPoint.toString()}";
              scaleUpdateDetails = details;
            });
          },
          onScaleEnd: (details) {
            print(details);
            setState(() {
              scaleUpdateDetails = ScaleUpdateDetails();
            });
          },
          child: Container(
            color: Colors.red,
            width: double.infinity,
            height: double.infinity,
            child: Center(
                // 组件的动画效果
              child: Transform(
                transform: Matrix4.rotationZ(scaleUpdateDetails.rotation)
                    .scaled(
                        scaleUpdateDetails.scale),
                child: Text(
                  offsetText,
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 20,
                  ),
                ),
              ),
            ),
          ),
        ),
复制代码

在onScaleUpdate方法中获取ScaleUpdateDetails对象,其包含了两触点实现的缩放值scale、角度值rotation、偏移量focalPoint信息。通过上述代码并可实现手势再结合上Transform的组件动效,关于Transform不做过多介绍,主要为实现组件动画效果的组件,之后若有时间再做学习。

结尾

这篇只能算是手势功能基础篇,还有一些手势高阶功能实现未探究类似于Android中的手势冲突等一些功能性自定义开发,暂且先熟悉手势基本功能之后若有对这方面开发需求再做更深层次学习和研究。

关注下面的标签,发现更多相似文章
评论