美文网首页
深度解读Flutter手势系统

深度解读Flutter手势系统

作者: 第八区 | 来源:发表于2023-05-24 10:03 被阅读0次

前言

对于做为客户端开发,永远绕不开的两座大山:手势系统渲染系统。Flutter做为当前比较火的跨平台开发框架,学习它的手势系统也是很有必要的。当然网上也有一些讲解,你可能会看到手势竞争,竞争胜出的会消费事件,但却很少能把如何竞争,以及为什么它能胜出或者失败能够讲清楚,当自己要处理手势问题时还是无从下手,不知道重点在哪里。文章涉及的源码很多,会拿一些widget来举例。内容可能会有一点枯燥。

适合哪些人看?

  • 业务开发中遇到手势相关问题

  • 对Flutter手势分发感兴趣,想要了解底层实现原理

  • 面试需要

先做一道题开开胃

下面的代码运行后会屏幕中会出现一个红色方块,蓝色方块上覆盖着一个红色方块,请问分别进行以下操作,控制台的打印会是什么?

  1. 点击蓝色方块时

  2. 长按蓝色方块时


import 'package:flutter/material.dart';

void main() {

  runApp(const MyApp());

}

class MyApp extends StatelessWidget {

  const MyApp({super.key});

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      theme: ThemeData(

        primarySwatch: Colors.blue,

      ),

      home: const MyHomePage(title: '手势示例'),

    );

  }

}

class MyHomePage extends StatefulWidget {

  const MyHomePage({super.key, required this.title});

  final String title;

  @override

  State<MyHomePage> createState() => _MyHomePageState();

}

class _MyHomePageState extends State<MyHomePage> {

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text(widget.title),

      ),

      body: Container(

        alignment: Alignment.center,

        child: GestureDetector(

          onTapDown: (TapDownDetails details) {

            print("red onTapDown");

          },

          onTap: () {

            print("red onTap");

          },

          onLongPressDown: (LongPressDownDetails details){

            print("red onLongPressDown");

          },

          child: Container(

            color: Colors.red,

            height: 300,

            width: 300,

            alignment: Alignment.center,

            child: GestureDetector(

              onTapDown: (TapDownDetails details) {

                print("blue onTapDown");

              },

              onTap: () {

                print("blue onTap");

              },

              onLongPressDown: (LongPressDownDetails details){

                print("blue onLongPressDown");

              },

              child: Container(

                color: Colors.blue,

                height: 150,

                width: 150,

              ),

            ),

          ),

        ),

      ),

    );

  }

}

如果你发现打印的出乎你的意料,那你是否有兴趣进入Flutter的手势分发的世界!

手势分发

下面介绍Flutter手势分发的流程

示例一 onTapDown之无竞争手势

我们从一个简单的示例来入手,一个居中大小为300的红色方块


class _MyHomePageState extends State<MyHomePage> {

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text(widget.title),

      ),

      body: Container(

        alignment: Alignment.center,

        child: GestureDetector(

          onTapDown: (TapDownDetails details){

            print("red onTapDown");

          },

          child: Container(

            color: Colors.red,

            height: 300,

            width: 300,

          ),

        ),

      ),

    );

  }

}

我们打一个断点,点击蓝色的方块,看一下调用链:

[图片上传失败...(image-8b5b5b-1684980168825)]

[图片上传失败...(image-baa766-1684980168825)]

从调用链我们就能大概看到事件分发处理的流程

  • dispatchEvent

  • handleEvent

我们就从handlePointerEvent开始看,这个方法来自GestureBinding

如果看过flutter启动流程的同学应该知道,flutter定义了若干个Binding,如果处理手势的GestureBinding,渲染的RenderBinding,调度任务的SchedulerBinding。我们今天讲的手势系统,那理所应当就是在GestureBinding中


void handlePointerEvent(PointerEvent event) {

  //...

  _handlePointerEventImmediately(event);

}

我们继续进入到_handlePointerEventImmediately


void _handlePointerEventImmediately(PointerEvent event) {

  HitTestResult? hitTestResult;

  if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent || event is PointerPanZoomStartEvent) {

    hitTestResult = HitTestResult();

    hitTest(hitTestResult, event.position);//重点

    if (event is PointerDownEvent || event is PointerPanZoomStartEvent) {

      _hitTests[event.pointer] = hitTestResult;

    }

  } else if (event is PointerUpEvent || event is PointerCancelEvent || event is PointerPanZoomEndEvent) {

    hitTestResult = _hitTests.remove(event.pointer);

  } else if (event.down || event is PointerPanZoomUpdateEvent) {

    hitTestResult = _hitTests[event.pointer];

  }

  if (hitTestResult != null ||

      event is PointerAddedEvent ||

      event is PointerRemovedEvent) {

    dispatchEvent(event, hitTestResult); //重点

  }

}

手势事件的处理就在这个方法里了。我们先从第一个事件PointerDownEvent开始,整体就是2个步骤

  • 执行hitTest的到HitTestResult

  • dispatchEvent分发HitTestResult

下面先看hitTest

hitTest

GestureBinding的hitTest

我们先看它在GestureBinding中的定义是什么样的

[图片上传失败...(image-f0bd91-1684980168825)]

看到AS里的这两个箭头,应该能反应过来,它是一个覆写方法,同时还有子类覆写它。我们先看它继承自哪里,直接箭头点过去:

HitTestable


/// An object that can hit-test pointers.

abstract class HitTestable {

  // This class is intended to be used as an interface, and should not be

  // extended directly; this constructor prevents instantiation and extension.

  HitTestable._();

  /// Check whether the given position hits this object.

  ///

  /// If this given position hits this object, consider adding a [HitTestEntry]

  /// to the given hit test result.

  void hitTest(HitTestResult result, Offset position);

}

其实我觉得flutter的注释写的非常清楚了:一个可以测试pointers是否命中的对象

一个方法hitTest,用来检查给定位置是否命中该对象。如果这个给定的位置命中了这个对象,考虑添加一个[HitTestEntry],返回给定的HitTestResult。

RenderBinding的hitTest

前面我们看了继承类的hitTest,还是箭头直接点过去,我们发现实现是在RenderBinding中

如果有同学好奇为什么覆写跑到了RenderBinding中,可以了解下dart的mixin机制,WidgetsFlutterBinding的定义如下


class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

}```


```dart

@override

void hitTest(HitTestResult result, Offset position) {

 renderView.hitTest(result, position: position);

 super.hitTest(result, position);

}

_handlePointerEventImmediately中hitTest的关键流程

通过前面的hitTest的继承实现分析,我们的结论是在_handlePointerEventImmediately执行的hitTest逻辑是

  • renderView.hitTest(result, position: position);

  • GestureBindingresult.add(HitTestEntry(this));

    GestreueBinding把自己包装成HitTestEntry添加到了result中,这一点很重要,后面会用到

更多请查看 深度解读Flutter手势系统

相关文章

  • Flutter无限轮播Banner

    Flutter无限轮播Banner实现 [TOC] 手势 ​ Flutter中的手势系统有两个独立的层。第一层...

  • Flutter_点击事件和手势

    Flutter_点击事件和手势 Flutter中的手势系统有两个独立的层。第一层具有原始指针事件,其描述屏幕上指针...

  • 7 手势、自定义Widget

    手势 Flutter中的手势系统有两个独立的层。第一层为原始指针(pointer)事件,它描述了屏幕上指针(例如,...

  • Flutter 动画与打包

    动画与打包 动画 ​ Flutter中的动画系统基于Animation对象的,和之前的手势不同,它不是...

  • Flutter 4. 手势和资源文件

    1.手势 Flutter中提供的手势检测组件为GestureDetector,可以为child组件赋予手势响应。 ...

  • 动画与打包

    1、动画 Flutter中的动画系统基于Animation对象的,和之前的手势不同,它不是一个Widget,这是因...

  • flutter pushname使用和多参数传递

    配置routes参数 main函数: 使用 单参数 多参数 参考了:Flutter命名路由及传参的深度实践与解读[...

  • 8 动画与打包

    动画 Flutter中的动画系统基于Animation对象的,和之前的手势不同,它不是一个Widget,这是因为A...

  • Flutter 小技巧——手势处理

    Flutter手势处理 在Flutter中的手势事件分为两层。第一层有原始指针事件,它描述了屏幕上指针(例如,触摸...

  • Flutter 手势

    这一篇简单说说Flutter的手势。 自身支持手势的Widget 如果某个Widget本身支持事件监测,那直接给他...

网友评论

      本文标题:深度解读Flutter手势系统

      本文链接:https://www.haomeiwen.com/subject/kkvfedtx.html