Flutter 的吸收指针和忽略指针的作用以及区别

562 阅读5分钟

[》跳过拾光记忆]

拾光记忆

1. Flutter 项目资产管理,看这一篇就够了!

简介: 针对 Flutter 项目资产管理的脚本服务。Fam 具有以下特点: 支持多种平台以及各平台无差异化、界面美观、功能齐全、快捷方便。
推荐: ⭐️⭐️⭐️⭐️⭐️

2. Flutter 手势在多指触摸时一些方法会多次触发

简介: 针对 Flutter 多手指检测以及手势触发其他手势也触发的问题。
推荐: ⭐️⭐️⭐️⭐️⭐️

3. Dart 的枚举类型的高阶用法

简介: 这是让开发者更深入的了解 Dart 的枚举以及相关使用和方法。
推荐: ⭐️⭐️⭐️

4. Flutter 可以快速实现单项或者多项选择,你知道吗?

简介: 这是让开发者更加便捷的实现单选、多选功能,无需你对数据处理。
推荐: ⭐️⭐️⭐️⭐️⭐️

[返回拾光记忆《]

一、 简述

Flutter 应用开发中我们是否遇到过许多按钮、列表视图、列表卡片、许多容器等,它们在某些情况下需要同时禁止用户触摸交互,那我们该如何做呢?有的开发者立即还会想到可以把处理事件回调函数置为 null。是的,这是可以完成我们需求的,但是这种处理方式适合有很少量的交互处理并不适合有大量的交互处理的情况。下面我们将介绍如何优雅的去处理这种需求。

二、忽略指针 - IgnorePointer

  • 含义
    在命中测试期间不可见的小部件。
    注意:这里的不可见,不是看不到的意思。它的意思是实际存在,我们就当它不存在的意思

  • 作用
    它能忽略或者阻止整个小部件与树的交互以及其子小部件的指针事件。

  • 定义

    class IgnorePointer extends SingleChildRenderObjectWidget {
      const IgnorePointer({
        super.key,
        this.ignoring = true,
        this.ignoringSemantics,
        super.child,
      }) : assert(ignoring != null);
    
      final bool ignoring;
      final bool? ignoringSemantics;
    
      @override
      RenderIgnorePointer createRenderObject(BuildContext context) {
        return RenderIgnorePointer(
          ignoring: ignoring,
          ignoringSemantics: ignoringSemantics,
        );
      }
    
      @override
      void updateRenderObject(BuildContext context, RenderIgnorePointer renderObject) {
        renderObject
          ..ignoring = ignoring
          ..ignoringSemantics = ignoringSemantics;
      } 
    }
    

    从上面代码我们可以看到:

    • IgnorePointer 继承于 SingleChildRenderObjectWidget
    • IgnorePointerignoringignoringSemantics 这两个属性
    • ignoring 参数
      ignoring 默认为 true , 意思是在命中测试期间是否忽略此小部件。 (注意: 无论在命中测试期间是否忽略此小部件,它都会 在布局期间仍然占用空间并且在绘画期间可见。)
    • ignoringSemantics 参数
      编译语义树时是否忽略此小部件的语义。
  • 测试

    • 基础测试1

      IgnorePointer(
        child: GestureDetector(
          onTap: () {
            debugPrint('红色块内部事件触发');
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
          ),
        ),
      ),
      

      我们点击红色方块,而红色方块上的指针事件没有触发。我们将 IgnorePointerignoring 设置为 false, 代码如下:

      IgnorePointer(
        ignoring: false,
        child: GestureDetector(
          onTap: () {
            debugPrint('红色块内部事件触发');
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
          ),
        ),
      ),
      

      我们再次点击红色方块,而红色方块上的指针事件触发了。

    • 基础测试2

      IgnorePointer(
        ignoring: true,
        child: GestureDetector(
          onTap: () {
            debugPrint('红色块内部事件触发');
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
            child: Center(
              child: GestureDetector(
                onTap: () {
                  debugPrint('紫色色块内部事件触发');
                },
                child: Container(
                  height: 80,
                  width: 80,
                  color: Colors.purple,
                ),
              ),
            ),
          ),
        ),
      ),
      

      我们点击紫色的方块,紫色方块上的指针没有触发,红色方块上的指针也没有触发。
      这说明 IgnorePointer 可以忽略子组件下的所有组件的指针事件。
      我们将IgnorePointerignoring 设置为false 时,我们再次点击紫色以及红色,发现所有色块上的指针事件都能触发。

    • 基础测试3

      GestureDetector(
        onTap: () {
          debugPrint('红色块外层事件触发');
        },
        child: IgnorePointer(
          ignoring: true,
          child: GestureDetector(
            onTap: () {
              debugPrint('红色块被点击');
            },
            child: Container(
              height: 100,
              width: 100,
              color: Colors.red,
            ),
          ),
        ),
      ),
      

      当我们在 IgnorePointer 的外部添加指针事件,当IgnorePointerignoringTrue 是点击方块时,指针事件不能触发;反之可触发。

  • 总结
    IgnorePointer 可以忽略其所有子部件的指针事件,也阻断其父组件的指针事件。

三、吸收指针 - AbsorbPointer

  • 含义
    在命中测试期间吸收指针的小部件。

  • 作用
    吸收指针事件,阻止子部件中的指针事件。

  • 定义

    class AbsorbPointer extends SingleChildRenderObjectWidget {
      const AbsorbPointer({
        super.key,
        this.absorbing = true,
        super.child,
        this.ignoringSemantics,
      }) : assert(absorbing != null);
    
     
      final bool absorbing;
      final bool? ignoringSemantics;
    
      @override
      RenderAbsorbPointer createRenderObject(BuildContext context) {
        return RenderAbsorbPointer(
          absorbing: absorbing,
          ignoringSemantics: ignoringSemantics,
        );
      }
    
      @override
      void updateRenderObject(BuildContext context, RenderAbsorbPointer renderObject) {
        renderObject
          ..absorbing = absorbing
          ..ignoringSemantics = ignoringSemantics;
      }
    }
    

    从上面的代码看,我们有种似曾相识的感觉,这不是和 IgnorePointer 的定义很像吗?只是类名和其中一个属性不同,其他几乎一样。是的,它们很像。从上面分析,如下:

    • AbsorbPointer 也是继承于 SingleChildRenderObjectWidget
    • AbsorbPointerabsorbingignoringSemantics 这两个属性 (TM: ignoringSemantics - 属性名都不改了)
    • absorbing 属性
      absorbing默认值为 true ,此小部件阻止其子树接收通过终止自身的命中测试来触发指针事件。
  • 基础测试

    • 测试1

      AbsorbPointer(
        absorbing: true,
        child: GestureDetector(
          onTap: () {
            debugPrint('红色块被点击');
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
          ),
        ),
      )
      

      absorbingtrue 时,点击红色块,红色块上指针事件没有触发。反之,红色块上的指针触发。

    • 测试2

      AbsorbPointer(
        absorbing: true,
        child: GestureDetector(
          onTap: () {
            debugPrint('红色块被点击');
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
            child: GestureDetector(
              onTap: () {
                debugPrint('紫色块被点击');
              },
              child: Center(
                child: Container(
                  height: 80,
                  width: 80,
                  color: Colors.purple,
                ),
              ),
            ),
          ),
        ),
      )
      

      absorbingtrue 时,点击红色块和紫色块,红色块和紫色块上指针事件都没有触发。反之都能触发对应的指针事件。

    • 测试3

      GestureDetector(
        onTap: () {
          debugPrint('外部被点击');
        },
        child: AbsorbPointer(
          absorbing: true,
          child: GestureDetector(
            onTap: () {
              debugPrint('红色块被点击');
            },
            child: Container(
              height: 100,
              width: 100,
              color: Colors.red,
              child: GestureDetector(
                onTap: () {
                  debugPrint('紫色块被点击');
                },
                child: Center(
                  child: Container(
                    height: 80,
                    width: 80,
                    color: Colors.purple,
                  ),
                ),
              ),
            ),
          ),
        ),
      )
      

      absorbingtrue 时,点击红色块和紫色块,AbsorbPointer 的外部指针事件触发。反之都能触发对应的指针事件。

  • 总结
    AbsorbPointer 可以吸收其所有子部件上的指针事件,不能吸收其父组件的指针事件。

四、IgnorePointer 和 AbsorbPointer 的区分

  • 相同之处
    IgnorePointerAbsorbPointer 忽略和吸收其所有子组件的指针事件。

  • 不同之处
    IgnorePointer 可以忽略整个树上的指针事件; AbsorbPointer 不能吸收整个树上的指针事件。

五、 综述

经过我们对 IgnorePointerAbsorbPointer 了解,我们就可以很优雅的解决简述中提到的问题了。如果感觉可以,请留下你的爱心(❤️)。