Flutter-Web:鼠标悬停 -> 将光标更改为指针

Flutter-Web: Mouse hover -> Change cursor to pointer

更新(2021/05/11):

Flutter 现在原生实现了悬停事件 Widgets。

有一个用于 RaisedButton 等 Widget 的 MouseCursor 和 hoverColor 或 hoverElevation 等属性。

https://api.flutter.dev/flutter/rendering/MouseCursor-class.html

您还可以在已接受答案中所述的其他任何地方使用 InkWell。


原题:

如何在 Flutter 中更改光标外观? 我知道使用 Listener() 小部件我们可以监听鼠标事件, 但我还没有找到任何关于 flutter web 悬停事件的信息。 有人找到解决方案了吗?

我认为鼠标事件不会在网络上运行,Listener Widget 在 Google I/O 2019 年进行了演示并且可以使用鼠标,但它是作为 ChromeOS 应用程序而不是网络应用

根据Flutter web on GitHub

At this time, desktop UI interactions are not fully complete, so a UI built with flutter_web may feel like a mobile app, even when running on a desktop browser.

dev 频道构建版本 1.19.0–3 开始。0.pre 内置了对指针光标的支持。使用与波纹管相同的方法,不同之处在于应用于 Flutter 应用程序容器元素 flt-glass-pane。使用波纹管方法只会复制行为。

为了覆盖 pointer 光标,您可以使用波纹管方法,但应用于 flt-glass-pane 元素。

解决方法如下:

  1. 您必须在整个 body[=47= 上设置一个 id(例如 app-container ] 应用的 index.html 模板)。

这就是您的 index.html 的样子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My awesome app</title>
</head>
<body id="app-container">
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
  1. 接下来,您必须创建一个包装飞镖 class。我称它为hand_cursor.dart:
import 'package:flutter_web/gestures.dart';
import 'package:flutter_web/widgets.dart';
import 'package:universal_html/html.dart' as html;
// see https://pub.dev/packages/universal_html

class HandCursor extends MouseRegion {

  // get a reference to the body element that we previously altered 
  static final appContainer = html.window.document.getElementById('app-container');

  HandCursor({Widget child}) : super(
    onHover: (PointerHoverEvent evt) {
      appContainer.style.cursor='pointer';
      // you can use any of these: 
      // 'help', 'wait', 'move', 'crosshair', 'text' or 'pointer'
      // more options/details here: http://www.javascripter.net/faq/stylesc.htm
    },
    onExit: (PointerExitEvent evt) {
      // set cursor's style 'default' to return it to the original state
      appContainer.style.cursor='default';
    },
    child: child
  );

}
  1. 之后,无论您希望在何处显示手形光标,都必须将您的元素包装在此 HandCursor 包装器中。请参阅下面的 class awesome_button.dart
import 'package:awesome_app/widgets/containers/hand_cursor.dart';
import 'package:flutter_web/material.dart';
import 'package:flutter_web/widgets.dart';

class AwesomeButton extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        HandCursor(
          child: IconButton(
            onPressed: () {
              // do some magic
            },
            icon: Icon(Icons.star)
          ),
        )
      ],
    );
  }

}

可以找到一个简短的解释 here

可以在 here.

中找到一个更通用的更新,适用于使用 Flutter master 频道创建的新 Web 项目

希望对您有所帮助。

以前的方法已弃用。这是更新后的代码

import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:universal_html/prefer_sdk/html.dart' as html;

class HandCursor extends MouseRegion {
  static final appContainer = html.window.document.getElementById('app-container');
  HandCursor({Widget child})
      : super(
          onHover: (PointerHoverEvent evt) {
            appContainer.style.cursor = 'pointer';
          },
          onExit: (PointerExitEvent evt) {
            appContainer.style.cursor = 'default';
          },
          child: child,
        );
}

然后在您的 pubspec.yaml 文件中,将 universal_html 添加为包作为依赖项。版本可能会改变。

dependencies:
  flutter:
    sdk: flutter
  universal_html: ^1.1.4

您仍然希望将应用程序容器的 ID 附加到您的 html 的正文中。这是我的 html 文件。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Your App Title</title>
</head>
<body id="app-container">
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

您想将手形光标小部件的代码放在它自己的文件中。你可以称它为hand_cursor.dart。并在您希望手显示的小部件上使用它,将其导入您正在处理的文件并将您想要的小部件包装在 HandCursor 小部件中。

Constantin Stan 改编的答案

对于那些想要具有类似于 InkWell 小部件和边框半径选项的点击效果的用户:

添加到您的 pubspec.yaml 文件

dependencies:
  universal_html: ^1.1.4

然后将以下标签添加到 index.html 文件中 <body id="app-container"> 如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Your App Title</title>
</head>
<body id="app-container">
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

最后创建以下小部件并使用封装的所有必要小部件:

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:universal_html/prefer_sdk/html.dart' as html;

class InkWellMouseRegion extends InkWell {
  InkWellMouseRegion({
    Key key,
    @required Widget child,
    @required GestureTapCallback onTap,
    double borderRadius = 0,
  }) : super(
          key: key,
          child: !kIsWeb ? child : HoverAware(child: child),
          onTap: onTap,
          borderRadius: BorderRadius.circular(borderRadius),
        );
}

class HoverAware extends MouseRegion {

  // get a reference to the body element that we previously altered 
  static final appContainer = html.window.document.getElementById('app-container');

  HoverAware({Widget child}) : super(
    onHover: (PointerHoverEvent evt) {
      appContainer.style.cursor='pointer';
      // you can use any of these: 
      // 'help', 'wait', 'move', 'crosshair', 'text' or 'pointer'
      // more options/details here: http://www.javascripter.net/faq/stylesc.htm
    },
    onExit: (PointerExitEvent evt) {
      // set cursor's style 'default' to return it to the original state
      appContainer.style.cursor='default';
    },
    child: child
  );

}
final appContainer 
     = html.document.getElementsByTagName('body')[0] as html.Element;

            GestureDetector(
                        child: MouseRegion(
                          child: Text(
                            'https://github.com/yumi0629',
                            style: textStyle,
                          ),
                          onHover: (_) => appContainer.style.cursor = 'pointer',
                          onExit: (_) => appContainer.style.cursor = 'default',
                        ),
                        onTap: () {
                          print('open');
                          js.context.callMethod(
                              'open', ['https://github.com/yumi0629']);
                        },
                      )

我很难找到有关现在内置支持的文档。以下是对我有帮助的内容:https://github.com/flutter/flutter/issues/58260

这对我有用,没有改变 index.html 等

MouseRegion(
  cursor: SystemMouseCursors.click,
    child: GestureDetector(
      child: Icon(
        Icons.add_comment,
        size: 20,
        ),
      onTap: () {},
    ),
  ),

另见官方文档:https://api.flutter.dev/flutter/rendering/MouseCursor-class.html

Widget build(BuildContext context) {
  return Center(
    child: MouseRegion(
      cursor: SystemMouseCursors.text,
      child: Container(
        width: 200,
        height: 100,
        decoration: BoxDecoration(
          color: Colors.blue,
          border: Border.all(color: Colors.yellow),
        ),
      ),
    ),
  );
}

这里 https://api.flutter.dev/flutter/material/MaterialStateMouseCursor-class.html 官方文档中的另一个精彩示例“...定义了一个鼠标光标,当其小部件被禁用时解析为 SystemMouseCursors.forbidden。”

从 Flutter beta 版本 1.19.0-4.1.pre 开始,将 id 添加到 body 并设置其光标不起作用。因为 flt-glass-pane 正在替换光标。 所以解决办法就是直接设置cursor到flt-glass-pane.

以下是有效的更新。

class HandCursor extends MouseRegion {
    static final appContainer = html.window.document.querySelectorAll('flt-glass-pane')[0];
    HandCursor({Widget child}) : super(
        onHover: (PointerHoverEvent evt) {
            appContainer.style.cursor='pointer';
        },
        onExit: (PointerExitEvent evt) {
            appContainer.style.cursor='default';
        },
        child: child
    );    
}

您可以使用具有 onHover 事件的 InkWell

InkWell(
    onTap: () {},
    onHover: (value) {
      setState(() {
        isHovered = value;
      });
    },
    child: Container(
      width: 50,
      height: 72,
      color: Colors.black   
    )
);

确保有一些onTap,即使是一个空函数,否则它被认为是禁用的,悬停将不起作用

我所知道的最简单的方法

InkWell(
        onTap: (){},
        mouseCursor: MaterialStateMouseCursor.clickable,
        ...