Flutter中鼠标 onEnter onExit onHover 实现代码分析

这篇具有很好参考价值的文章主要介绍了Flutter中鼠标 onEnter onExit onHover 实现代码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

生活会给你任何最有益的经历,以助你意识的演变。

转载请注明出处: 这里对最近用到的一些 Flutter 开源的东西进行总结积累,希望能帮助到大家。

背景

Android设备在使用的时候,大家日常使用的都是手指触摸滑动,点击进行操作,但是实际上,系统为我们提供了鼠标操作的能力。我们使用蓝牙鼠标连接到手机就会在界面上出现一个鼠标样式,然后我们可以使用鼠标进行操作,Flutter 也对系统原生的这个特性进行了支持,可以在Flutter中监听和处理响应的事件。

同样,IOS 也同样也可以使用鼠标进行连接,进行使用苹果设置指针样式

测试代码

这里自己编写了一个测试界面,我们可以使用监听鼠标进入和退出这个 View 的次数,同时当鼠标在 View 上移动的时候,我们监听 Hover 事件,并打印出对应的日志。
flutter 鼠标悬停触发,flutter
测试代码:下面的代码我们贴到我们的 flutter 工程的 main.dart 文件中,就可以运行的到上面的测试App。

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

/// Flutter code sample for [MouseRegion].

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Nested MouseRegion Example',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Nested MouseRegion Example'),
        ),
        body: Center(
          child: ParentWidget(),
        ),
      ),
    );
  }
}

class ParentWidget extends StatefulWidget {
  
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  int parentEnterCount = 0;
  int parentExitCount = 0;

  void handleParentEnter() {
    setState(() {
      parentEnterCount++;
    });
  }

  void handleParentExit() {
    setState(() {
      parentExitCount++;
    });
  }

  int childEnterCount = 0;
  int childExitCount = 0;

  void handleChildEnter() {
    setState(() {
      childEnterCount++;
    });
  }

  void handleChildExit() {
    setState(() {
      childExitCount++;
    });
  }

  void onParentHover(){
    print("parent onHover");
  }

  void onChildHover(){
    print("child onHover");
  }

  
  Widget build(BuildContext context) {
    return MouseRegion(
      cursor: SystemMouseCursors.click,
      onEnter: (_) => handleParentEnter(),
      onExit: (_) => handleParentExit(),
      onHover: (_) => onParentHover(),
      child: Container(
        width: 200,
        height: 200,
        color: Colors.blue,
        child: Stack(
          children: [
            Positioned(
              top: 50,
              left: 50,
              child: MouseRegion(
                onEnter: (_) => handleChildEnter(),
                onExit: (_) => handleChildExit(),
                onHover: (_) => onChildHover(),
                child: Container(
                  width: 100,
                  height: 100,
                  color: Colors.red,
                ),
              ),
            ),
            Positioned(
              bottom: 0,
              child: Container(
                width: 200,
                color: Colors.black54,
                padding: EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Parent: Enter Count - $parentEnterCount, Exit Count - $parentExitCount',
                      style: TextStyle(color: Colors.white),
                    ),
                    SizedBox(height: 8),
                    Text(
                      'Child: Enter Count - $childEnterCount, Exit Count - $childExitCount',
                      style: TextStyle(color: Colors.white),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

这里我们用到了 Flutter 提供的 MouseRegion Widget 。

  • onEnter property
    Triggered when a mouse pointer has entered this widget
  • onExit property
    Triggered when a mouse pointer has exited this widget when the widget is still mounted.
  • onHover property
    Triggered when a pointer moves into a position within this widget without buttons pressed.
    上面这几个方法对应的功能描述如上。

flutter 代码

onEnter & onExit

鼠标移入和移出是一个成对的监听事件,查看源码实现我们先断点查看流程:
flutter 鼠标悬停触发,flutter
在事件进行分发的时候,鼠标 Enter 和 Exit 的掉用都会在MouseTracker 中进行处理。因此onEnter 和 onExit 的实现我们也主要查看该模块的实现。
几个相关的概念需要理解

  • MouseTrackerAnnotation :The annotation object used to annotate regions that are interested in mouse movements.
    其实查看代码,这个类中会存储我们注册的回掉,也就意味着对应的对象在监听鼠标相关的事件,因此在回掉的时候我们也是通过这个对象完成。
    flutter 鼠标悬停触发,flutter
  • MouseState: Various states of a connected mouse device used by [MouseTracker].
    flutter 鼠标悬停触发,flutter
    MouseState 中的数据会在派发的时候使用

hittest 指的是命中检测,即从当前的位置为标准检测鼠标的位置可以命中哪个View。

  • MouseTrackerUpdateDetails :This class contains the information needed to handle the update that might change the state of a mouse device
    查看代码,MouseTrackerUpdateDetails 中主要有
    flutter 鼠标悬停触发,flutter
    可以看到 MouseTrackerUpdateDetails 其实主要就是存储 Mouse 状态更新是所需要的信息。

代码流程:
拿到一个事件以后事件派发的流程如下:

mouse_tracker.dart
// 该方法会提供给 RendererBinding ,在事件派发的时候先掉用该方法作为处理的入口。如果是多设备参考updateAllDevices,方法,流程基本差不多
void updateWithEvent(...){
	... 
	// 这里首先通过 hittest 拿到当前位置命中检测的结果。
	final HitTestResult result;
    if (event is PointerRemovedEvent) {
      result = HitTestResult();
    } else {
      final int viewId = event.viewId;
      result = hitTestResult ?? _hitTestInView(event.position, viewId);
    }
	...
		// 拿到鼠标对应的目标状态
	    final _MouseState targetState = _mouseStates[device] ?? existingState!;
		
		// 更新 MouseState 中存储的事件为最新的事件
        final PointerEvent lastEvent = targetState.replaceLatestEvent(event);

		// 这里将hittest 的结果转换为 Annotations 的集合
        final LinkedHashMap<MouseTrackerAnnotation, Matrix4> nextAnnotations = event is PointerRemovedEvent ?
            LinkedHashMap<MouseTrackerAnnotation, Matrix4>() :
            _hitTestInViewResultToAnnotations(result);
         // 这里替换 MouseState 中对应的状态
        final LinkedHashMap<MouseTrackerAnnotation, Matrix4> lastAnnotations = targetState.replaceAnnotations(nextAnnotations);
		
		// 这个会掉到 -> _handleDeviceUpdateMouseEvents
        _handleDeviceUpdate(_MouseTrackerUpdateDetails.byPointerEvent(
          lastAnnotations: lastAnnotations,
          nextAnnotations: nextAnnotations,
          previousEvent: lastEvent,
          triggeringEvent: event,
        ));
}


// 进行事件最后的派发处理
static void _handleDeviceUpdateMouseEvents(_MouseTrackerUpdateDetails details) {
	final PointerEvent latestEvent = details.latestEvent;

	// 当前对应的对象集合
    final LinkedHashMap<MouseTrackerAnnotation, Matrix4> lastAnnotations = details.lastAnnotations;
    // 下一次检测结果对应的对象集合
    final LinkedHashMap<MouseTrackerAnnotation, Matrix4> nextAnnotations = details.nextAnnotations;
	
	// 当前结果有这个对象,但是下一次检测结果中没有,就派发exit事件
	lastAnnotations.forEach((MouseTrackerAnnotation annotation, Matrix4 transform) {
      if (!nextAnnotations.containsKey(annotation)) {
        if (annotation.validForMouseTracker && annotation.onExit != null) {
          annotation.onExit!(baseExitEvent.transformed(lastAnnotations[annotation]));
        }
      }
    });
	// 上一次检测结果中没有这个对象,下一次检测结果中包含则派发 onEnter事件
	final List<MouseTrackerAnnotation> enteringAnnotations = nextAnnotations.keys.where(
      (MouseTrackerAnnotation annotation) => !lastAnnotations.containsKey(annotation),
    ).toList();
    final PointerEnterEvent baseEnterEvent = PointerEnterEvent.fromMouseEvent(latestEvent);
  // Order is important for mouse event callbacks. The
    // `_hitTestInViewResultToAnnotations` returns annotations in the visual order
	// 这里 需要 reversed 是因为 hittest 结果是视觉顺序,例如这样:child1->parent,但是进入的时候是先进入 parent 然后再进入到 child,所以要反向一下
    for (final MouseTrackerAnnotation annotation in enteringAnnotations.reversed) {
      if (annotation.validForMouseTracker && annotation.onEnter != null) {
        annotation.onEnter!(baseEnterEvent.transformed(nextAnnotations[annotation]));
      }
    }
}

最后,我们注册的onEnter 和onExit 函数就会被调用。

onHover

当鼠标在 View 上移动,并没有按下的时候,这个事件就会被调用:
hover 的分发相对简单一些,直接调用 dispatchEvent() 去将事件分发到 hittest 的结果: MouseRegion 对象上。

  // GestureBinding::dispatchEvent
  void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
  ...
   for (final HitTestEntry entry in hitTestResult.path) {
      try {
        entry.target.handleEvent(event.transformed(entry.transform), entry);
        ...
	}
}

// 在RenderMouseRegion 的handleEvent 中进行 hover 事件的处理
  
  void handleEvent(PointerEvent event, HitTestEntry entry) {
    assert(debugHandleEvent(event, entry));
    if (onHover != null && event is PointerHoverEvent) {
      return onHover!(event);
    }
  }

End

flutter 对 Mouse 状态监听的实现不算复杂,大家可以使用本文进行参考。文章来源地址https://www.toymoban.com/news/detail-785574.html

到了这里,关于Flutter中鼠标 onEnter onExit onHover 实现代码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Deferred Components-实现Flutter运行时动态下发Dart代码 | 京东云技术团队

    Deferred Components,官方实现的Flutter代码动态下发的方案。本文主要介绍官方方案的实现细节,探索在国内环境下使用Deferred Components,并且实现了最小验证demo。读罢本文,你就可以实现Dart文件级别代码的动态下发。 Deferred Components是Flutter2.2推出的功能,依赖于Dart2.13新增的对

    2024年02月06日
    浏览(37)
  • flutter中鼠标检测事件的应用---主要在于网页端使用

    flutter中鼠标检测事件的应用—主要在于网页端使用 鼠标放上去 主要代码 使用

    2024年04月11日
    浏览(40)
  • Flutter 调试工具篇 | 壹 - 使用 Flutter Inspector 分析界面

    1. 前言 很多朋友可能在布局过程中、或者组件使用过程中,会遇到诸如颜色、尺寸、约束、定位等问题,可能会让你抓耳挠腮。俗话说,磨刀不误砍柴工,会使用工具是非常重要的,其实 Flutter 提供了强大的调试工具,可以辅助我们去查看界面布局中的一切细节。 基于这些

    2024年02月15日
    浏览(44)
  • 常用框架分析(7)-Flutter

    link 主要对目前市面上常见的框架进行分析和总结,希望有兴趣的小伙伴们可以看一下,会持续更新的。希望各位可以监督我,我们一起学习进步。 Flutter是由Google开发的一个开源移动应用软件开发框架,用于创建高性能、高保真度的Android和iOS应用。它使用Dart语言编写,具有

    2024年02月10日
    浏览(29)
  • Flutter源码分析笔记:Widget类源码分析

    Flutter源码分析笔记 Widget类源码分析 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.net Email: 291148484@163.com. Shenzhen China Address of this article: https://blog.csdn.net/qq_28550263/article/details/132259681 【介绍】:本文记录阅读与分析Flutter源码 - Widget类源码分析。 Widget类是Flu

    2024年02月12日
    浏览(45)
  • Flutter 组件分析之SafeArea

    本系列教程基于Flutter widget, 意在为Flutter 入门提供基础建设. 重点在讲解widget 的 用法,参数以及扩展. 适宜人群: 入门 SafeArea 用于 填充类似于刘海屏、异形屏之类的屏幕的边距 , 在其中加入 padding . 保证多端多设备中展示不受影响. 同时, 这样操作也可以保证在多数情况下. 跨平

    2024年02月02日
    浏览(29)
  • Flutter 项目创建、运行及结构分析

    目录 开发工具  创建项目   1.New Flutter Project         1.1直接创建新项目         1.2 已有项目创建新项目   2.选择SDK,补充项目资料   3.Demo已生成         3.1 android 目录         3.2 ios目录         3.3 lib目录             3.4 test 目录(可先不管) 4.配置文件    

    2024年02月11日
    浏览(82)
  • flutter 代码混淆

    Flutter 应用混淆: Flutter 应用的混淆非常简单,只需要在构建 release 版应用时结合使用 --obfuscate 和 --split-debug-info 这两个参数即可。 –obfuscate --split-debug-info 用来指定输出调试文件的位置,该命令会生成一个符号映射表。目前支持 apk,appbundle,ios 和 ios-framework 等目标平台(

    2024年02月04日
    浏览(31)
  • ​flutter 代码混淆

    Flutter 应用混淆:Flutter 应用的混淆非常简单,只需要在构建 release 版应用时结合使用 --obfuscate 和 --split-debug-info 这两个参数即可。–obfuscate --split-debug-info 用来指定输出调试文件的位置,该命令会生成一个符号映射表。目前支持 apk,appbundle,ios 和 ios-framework 等目标平台(m

    2024年02月04日
    浏览(30)
  • 某Flutter-APP逆向分析

    图例: Flutter 打包后的so文件所在位置 使用IDA打开 libflutter.so文件,在 Srings window中搜索 ssl_server字符串,通过交互引用,可以查找到具体的引用函数,如下所示: 主要思路:Flutter框架通过中间人proxy无法拦截到数据包,需绕过其对证书绑定的判断。通过对libflut

    2024年02月13日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包