Multitouch Tap Gestures in Flutter

I've recently been playing around with Flutter – Google's open-source app development SDK for both iOS and Android - and have been very much enjoying it. It makes use of the Dart programming language and has so far proven great for quickly building cross-platform prototypes, however, given its immaturity it's not surprisingly a little rough around the edges, especially when it comes to more complex interactions.

I was trying to figure out how to handle taps with multiple fingers and eventually came to the conclusion that it just isn't something that's supported by the SDK as it stands. The proper way to implement this currently would be to write custom platform-specific code and hook it into your Flutter code. That being said, below I present a hacky, and likely not particularly robust solution I managed to get working by making a custom MultiTapGestureRecognizer subclass.

import "package:flutter/gestures.dart";

class MultiTouchGestureRecognizer extends MultiTapGestureRecognizer {
  MultiTouchGestureRecognizerCallback onMultiTap;
  var numberOfTouches = 0;
  int minNumberOfTouches = 0;

  MultiTouchGestureRecognizer() {
    super.onTapDown = (pointer, details) => this.addTouch(pointer, details);
    super.onTapUp = (pointer, details) => this.removeTouch(pointer, details);
    super.onTapCancel = (pointer) => this.cancelTouch(pointer);
    super.onTap = (pointer) => this.captureDefaultTap(pointer);
  }

  void addTouch(int pointer, TapDownDetails details) {
    this.numberOfTouches++; 
  }

  void removeTouch(int pointer, TapUpDetails details) {
    if (this.numberOfTouches == this.minNumberOfTouches) {
      this.onMultiTap(true);
    }
    else if (this.numberOfTouches != 0) {
      this.onMultiTap(false);
    }

    this.numberOfTouches = 0;
  }

  void cancelTouch(int pointer) {
    this.numberOfTouches = 0;
  }

  void captureDefaultTap(int pointer) {}
  
  @override
  set onTapDown(_onTapDown) {}

  @override
  set onTapUp(_onTapUp) {}

  @override
  set onTapCancel(_onTapCancel) {}

  @override
  set onTap(_onTap) {}
}

typedef MultiTouchGestureRecognizerCallback = void Function(bool correctNumberOfTouches);

That, as an example, can be used within a custom StatelessWidget, as in the code below.

import "package:flutter/material.dart";
import "./multiTouchGestureRecognizer.dart";

class MultiTapButton extends StatelessWidget {

  final MultiTapButtonCallback onTapCallback;
  final int minTouches;
  final Color backgroundColor;
  final Color borderColor;

  MultiTapButton(this.backgroundColor, this.borderColor, this.minTouches, this.onTapCallback);

  void onTap(bool correctNumberOfTouches) {
    print("Tapped with " + correctNumberOfTouches + " finger(s)");
    this.onTapCallback(correctNumberOfTouches);
  }

  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
    gestures: {
      MultiTouchGestureRecognizer: GestureRecognizerFactoryWithHandlers<
          MultiTouchGestureRecognizer>(
        () => MultiTouchGestureRecognizer(),
        (MultiTouchGestureRecognizer instance) {
          instance.minNumberOfTouches = this.minTouches;
          instance.onMultiTap = (correctNumberOfTouches) => this.onTap(correctNumberOfTouches);
        },
      ),
    },
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Expanded(
        child: 
            Container(
              padding: EdgeInsets.all(12.0),
              decoration: BoxDecoration(
                color: this.backgroundColor,
                border: Border(
                  top: BorderSide(width: 1.0, color: borderColor),
                  left: BorderSide(width: 1.0, color: borderColor),
                  right: BorderSide(width: 1.0, color: borderColor),
                  bottom: BorderSide(width: 1.0, color: borderColor),
                ),
              ),
              child: Text("Tap with " + correctNumberOfTouches + " finger(s).", textAlign: TextAlign.center),
            ),
          ),
      ]),
    );
  }
}

typedef MultiTapButtonCallback = void Function(bool correctNumberOfTouches);

Again, this is hacky, and I wouldn't advise it be used for production purposes, but for quick demos and POCs please feel free to take it and run with it.

NOTE: I did notice 3+ fingers initially not working on an HTC phone (U Ultra) before I disabled some gesture navigation feature that's on by default. So if you run into any trouble perhaps double check any gesture-based features that are enabled on your phone.