Flutter自定义光标插件flutter_custom_cursor的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter自定义光标插件flutter_custom_cursor的使用

插件概述

flutter_custom_cursor 是一个允许直接从内存缓冲区创建/设置自定义鼠标光标的插件。非常感谢 imiskolee 创建了这个插件的基础。

Pub 版本 Pub 发布者

支持平台

  • ✅ macOS
  • ✅ Windows
  • ✅ Linux

注意: 目前,Windows 平台需要的 API 包含在 Flutter 的 master 分支中。这意味着你需要在 Windows 平台上使用 Flutter master 分支来使用此插件。详情请参阅 flutter engine PR#36143

更新:最新的 Flutter 3.7.0 不包含上述 PR,该 PR 合并到了 flutter-3.7.0-candidate.2,而 Flutter stable 3.7.0 使用的是 flutter-3.7.0-candidate.1。这一限制可能会在下一个 Flutter 稳定版发布时解除。

准备工作

注册自定义光标

在使用自定义光标之前,您需要先注册它。下面是一个示例代码:

// register this cursor
cursorName = await CursorManager.instance.registerCursor(CursorData()
  ..name = "test"
  ..buffer =
      Platform.isWindows ? memoryCursorDataRawBGRA : memoryCursorDataRawPNG
  ..height = img.height
  ..width = img.width
  ..hotX = 0
  ..hotY = 0);

registerCursor 函数会返回一个 String 类型的 cacheName,可以用于设置或删除这个光标。

CursorData.buffer 是一个 Uint8List,包含了光标数据。请注意,在 Windows 上,buffer 格式为 rawBGRA,而在其他操作系统上则为 rawPNG

设置自定义光标

我们实现了 FlutterCustomMemoryImageCursor 类,它是 MouseCursor 的子类。这个类会自动为你设置内存中的光标。使用方法如下:

MouseRegion(
  cursor: FlutterCustomMemoryImageCursor(key: cursorName),
  child: Row(
    children: [
      Text("Memory image here", style: style),
    ],
  ),
),

删除光标

你可以通过 cursorName 来删除一个光标:

await CursorManager.instance.deleteCursor("cursorName");

示例项目

以下是一个完整的示例项目,展示了如何使用 flutter_custom_cursor 插件:

import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/material.dart' hide Image;
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_custom_cursor/cursor_manager.dart';
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
import 'package:image/image.dart' as img2;

late Uint8List memoryCursorDataRawBGRA;
late Uint8List memoryCursorDataRawPNG;
late String cursorName;

late img2.Image img;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // read memory Cursor
  print("reading memory cursor");
  final byte = await rootBundle.load("assets/cursors/data.png");
  memoryCursorDataRawPNG = byte.buffer.asUint8List();
  img = await getImage(memoryCursorDataRawPNG);
  memoryCursorDataRawBGRA =
      (img.getBytes(format: img2.Format.bgra)).buffer.asUint8List();
  
  // register this cursor
  cursorName = await CursorManager.instance.registerCursor(CursorData()
    ..name = "test"
    ..buffer =
        Platform.isWindows ? memoryCursorDataRawBGRA : memoryCursorDataRawPNG
    ..height = img.height
    ..width = img.width
    ..hotX = 0
    ..hotY = 0);

  // test delete
  print("test delete");
  await CursorManager.instance.deleteCursor("test");

  cursorName = await CursorManager.instance.registerCursor(CursorData()
    ..name = "test"
    ..buffer =
        Platform.isWindows ? memoryCursorDataRawBGRA : memoryCursorDataRawPNG
    ..height = img.height
    ..width = img.width
    ..hotX = 0
    ..hotY = 0);

  runApp(const MyApp());
}

Future<img2.Image> getImage(Uint8List bytes) async {
  img = img2.decodePng(bytes)!;
  return img;
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  String msg = "";

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  Future<void> initPlatformState() async {
    String platformVersion;
    try {
      platformVersion = 'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    var style = const TextStyle(fontSize: 30);
    print("rebuild");
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
            child: ListView(
          children: [
            MouseRegion(
              cursor: FlutterCustomMemoryImageCursor(key: cursorName),
              child: Row(
                children: [
                  Text("Memory Image Here", style: style),
                ],
              ),
            ),
            Text("OUTPUT: ${msg}"),
          ],
        )),
      ),
    );
  }
}

以上就是关于 flutter_custom_cursor 插件的使用介绍和完整示例。希望对您有所帮助!


更多关于Flutter自定义光标插件flutter_custom_cursor的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义光标插件flutter_custom_cursor的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用flutter_custom_cursor插件的一个简单示例。这个插件允许你自定义光标样式和行为,非常适合需要特殊光标行为的应用场景。

首先,确保你已经将flutter_custom_cursor插件添加到你的Flutter项目中。你可以在你的pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_custom_cursor: ^最新版本号  # 替换为最新版本号

然后运行flutter pub get来安装插件。

接下来,你可以在你的Flutter应用中使用这个插件。以下是一个简单的示例,展示如何自定义光标:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CustomCursorExample(),
    );
  }
}

class CustomCursorExample extends StatefulWidget {
  @override
  _CustomCursorExampleState createState() => _CustomCursorExampleState();
}

class _CustomCursorExampleState extends State<CustomCursorExample> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Custom Cursor Example'),
      ),
      body: CustomCursor(
        builder: (context, cursorController) {
          return GestureDetector(
            onPanUpdate: (details) {
              cursorController.updatePosition(details.globalPosition);
            },
            child: Stack(
              children: [
                // 你的内容
                Center(
                  child: Text(
                    'Hover over this text',
                    style: TextStyle(fontSize: 24),
                  ),
                ),
                // 自定义光标
                Positioned(
                  top: cursorController.position.dy - 10,  // 根据需要调整光标位置
                  left: cursorController.position.dx - 10, // 根据需要调整光标位置
                  child: Container(
                    width: 20,
                    height: 20,
                    decoration: BoxDecoration(
                      color: Colors.blue,
                      shape: BoxShape.circle,
                    ),
                  ),
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 使用CustomCursor包装我们的主要内容。CustomCursorbuilder参数接受一个函数,该函数返回一个包含手势检测和光标渲染的Widget
  2. 使用GestureDetector来监听手势事件,并在onPanUpdate回调中更新光标的位置。
  3. 使用Stack来叠加我们的内容和自定义光标。光标的位置通过cursorController.position获取,并根据需要调整。
  4. 自定义光标是一个简单的蓝色圆形Container,但你可以根据需要自定义它的形状、颜色和其他属性。

这个示例展示了基本的自定义光标实现。根据你的具体需求,你可以进一步扩展和自定义光标的行为和外观。

回到顶部