CustomCursor for Flutter Web
By: Constantin Stan
Tags: Web Development, Flutter, Dart, Flutter Widget, Flutter Web

CustomCursor for Flutter Web

How to customize the cursor in your web applications

A while back I've published an article that detailed a hack that enabled the use of the hand cursor in a Flutter for web project.

Now, I'm updating that widget (and hack) to work within the new web projects that can be built using the master channel of Flutter and also support more than one type of cursor.

⚠ Starting with dev channel build version 1.19.0–3.0.pre there is built-in support for the pointer cursor. ⚠

The same simple 3 steps:

1. You still have to set an id (for example app-container on the entire body of the app’s index.html template).

This is how your index.html will look like:

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>CustomCursor</title>

</head>

<body id="app-container">

<script src="main.dart.js" type="application/javascript"></script>

</body>

</html>

2. Next, you have to create a wrapper Dart class (a widget). I called it custom_cursor.dart:

import 'package:flutter/foundation.dart';

import 'package:flutter/gestures.dart';

import 'package:flutter/widgets.dart';

import 'package:universal_html/html.dart' as html; class CustomCursor extends MouseRegion { static final appContainer = html.window.document.getElementById('app-container');

static final String pointer = 'pointer';

static final String auto = 'auto';

static final String move = 'move';

static final String noDrop = 'no-drop';

static final String colResize = 'col-resize';

static final String allScroll = 'all-scroll';

static final String notAllowed = 'not-allowed';

static final String rowResize = 'row-resize';

static final String crosshair = 'crosshair';

static final String progress = 'progress';

static final String eResize = 'e-resize';

static final String neResize = 'ne-resize';

static final String text = 'text';

static final String nResize = 'n-resize';

static final String nwResize = 'nw-resize';

static final String help = 'help';

static final String verticalText = 'vertical-text';

static final String sResize = 's-resize';

static final String seResize = 'se-resize';

static final String inherit = 'inherit';

static final String wait = 'wait';

static final String wResize = 'w-resize';

static final String swResize = 'sw-resize'; // cursor types from http://www.javascripter.net/faq/stylesc.htm static final String pointer = 'pointer';static final String auto = 'auto';static final String move = 'move';static final String noDrop = 'no-drop';static final String colResize = 'col-resize';static final String allScroll = 'all-scroll';static final String notAllowed = 'not-allowed';static final String rowResize = 'row-resize';static final String crosshair = 'crosshair';static final String progress = 'progress';static final String eResize = 'e-resize';static final String neResize = 'ne-resize';static final String text = 'text';static final String nResize = 'n-resize';static final String nwResize = 'nw-resize';static final String help = 'help';static final String verticalText = 'vertical-text';static final String sResize = 's-resize';static final String seResize = 'se-resize';static final String inherit = 'inherit';static final String wait = 'wait';static final String wResize = 'w-resize';static final String swResize = 'sw-resize'; CustomCursor({Widget child, String cursorStyle='pointer'}) : super(

onHover: (PointerHoverEvent evt) {

if(kIsWeb) {

appContainer.style.cursor=cursorStyle;

}

},

onExit: (PointerExitEvent evt) {

if(kIsWeb) {

appContainer.style.cursor='default';

}

},

child: child

); }

The code is quite simple: we extend the MouseRegion class and capture a reference to the appContainer (the html body element we just set the app-container id on); then we make sure to listen the hover/exit events, if we're in a web browser, and change the cursor’s style accordingly (cursorStyle/default) and pass on the child.

Previously we made use of the Listener class and its onPointerHover and onPointerExit events, though that is deprecated.

As a choice of cursor styles there are plenty: help, wait, move, crosshair, text, pointer, and others. More choices and details on this can be found here.

3. After that, wherever you want to have the custom cursor shown, you have to wrap your element in the CustomCursor widget.

See the awesome_button.dart class bellow:

import 'package:cursor_demo/utils/custom_cursor.dart';

import 'package:flutter/material.dart';

@override

Widget build(BuildContext context) {

return Stack(

children: <Widget>[

CustomCursor(

cursorStyle: CustomCursor.text,

child: IconButton(

onPressed: () {

// now others also know

// that you can

// do some magic

},

icon: Icon(Icons.star, color: Colors.blue,)

),

),

],

);

}

} class AwesomeButton extends StatelessWidget {Widget build(BuildContext context) {return Stack(children: [cursorStyle: CustomCursor.text,child: IconButton(onPressed: () {// now others also know// that you can// do some magic},icon: Icon(Icons.star, color: Colors.blue,)),),],);

In the above example, if the cursorStyle is omitted, it'll default to the pointer style.

Also, if you only target the web platform, you could use the dart:html package instead of the universal_html.

Built-in support

Starting with dev channel build version 1.19.0–3.0.pre there is built-in support for the pointer cursor. The method used is the same as above but applied on the Flutter web app container element flt-glass-pane . Using the above method will just duplicate the behavior.

To override the pointer cursor, you can use the previously described method but applied on the flt-glass-pane element.

Tha(nk|t’)s all!