Flutter library to add gestures and animations to each Shape you draw on your canvas in your CustomPainter

Overview

Flutter library to bring your CustomPainter 🎨 to Life ⚡️

touchable library gives you the ability to add various gestures and animations to each Shape you draw on your canvas in the CustomPainter


           

Index :

Why Use Touchable ?

  • The CustomPainter lets you only draw shapes on the canvas. But most would want to let user interact with the drawings.

  • Add all kinds of gesture callbacks to each drawing to give interaction capability to the shapes you draw on the canvas.

  • Animating individual shapes has never been this easy.

  • Auto Handles the painting style (filled ▮ , stroke ▯) and detects touch accordingly.

  • Handles Painting stroke width. So if your shapes are painted thick , we still got it covered ✓

  • Supports clipping and different clipping modes for the drawings.

  • Supports HitTestBehavior for each shape.

  • Simple and Easy API. Just wrap your CustomPaint with CanvasTouchDetector and use the TouchyCanvas in your painter.

With touchable , you get what the normal canvas always missed : touchability 😉

Installation

Add the touchable package as dependency in your pubspec.yaml

dependencies:
  touchable:

Usage

  • Just Wrap your CustomPaint widget with CanvasTouchDetector. It takes a builder function as argument that expects your CustomPaint widget as shown below.
CanvasTouchDetector(
    builder: (context) => 
        CustomPaint(
            painter: MyPainter(context)
        )
)
  • Inside your CustomPainter class's paint method , create and use the TouchyCanvas object (using the context obtained from the CanvasTouchDetector and canvas) to draw any shape with different gesture callbacks.
var myCanvas = TouchyCanvas(context,canvas);
myCanvas.drawRect( rect , Paint() , onTapDown: (tapDetail){
    //Do stuff here. Probably change your state and animate
});

MyPainter example :

class MyPainter extends CustomPainter {

  final BuildContext context ;
  MyPainter(this.context); // context from CanvasTouchDetector

  @override
  void paint(Canvas canvas, Size size) {
    var myCanvas = TouchyCanvas(context,canvas); 

    myCanvas.drawCircle(Offset(10, 10), 60, Paint()..color=Colors.orange ,
        onTapDown: (tapdetail) {
         print("orange Circle touched");
       },
        onPanDown:(tapdetail){
          print("orange circle swiped");
        } 
    );

    myCanvas.drawLine(
        Offset(0, 0),
        Offset(size.width - 100, size.height - 100),
        Paint()
          ..color = Colors.black
          ..strokeWidth = 50, 
        onPanUpdate: (detail) {
            print('Black line Swiped'); //do cooler things here. Probably change app state or animate
    });
  }
}

Read the article on Medium : Bring Your CustomPainter to Life using Touchable



How Touchable Works

When you draw shapes on the canvas (TouchyCanvas) , it keeps track of the dimensions of each shape you draw and their painting style , stroke , order , clippings etc.

When user performs any gesture on the screen , based on the location of the gesture , the appropriate shape is selected from the lot taking clipping regions , paint , hitTest behaviour etc into account in an optimized way. Callbacks of the corresponding shapes (one or more depending on the hitTest behavior) are executed.

Road Map

  • Basic Shape Detection
    • Line
    • Rectangle (Rect)
    • Circle
    • Oval or Ellipse
    • Arc
      • segment
      • sector
    • Rounded Rectangle (RRect)
    • Custom Path [only supports opaque hittest]
    • Points (PointMode.points , PointMode.lines , PointMode.polygon)
    • Vertices
      • Traingle
      • Traingle Strip
      • Traingle Fan
  • Support for proper edge detection based on the Paint object properties :
    • Paint style
    • Stroke Width
    • Stroke Cap
      • StrokeCap to draw Points
      • StrokeCap.round for lines with huge width.
  • Support Clipping and clipping modes
    • ClipRect
      • intersect mode [Touch detection enabled only inside the clipped region]
      • difference mode [Touch detection enabled only outside the clipped region]
    • ClipRRect
    • ClipPath
  • Support for HitTestBehavior
  • Make the touch detection handling to run in a seperate isolate.
  • Support for translation , rotation , scaling and skewing transformations that needs some vector math

Links

Comments
  • Add support for Null Safety

    Add support for Null Safety

    This is not a bug or issue, but i wanted to upgrade my current application to Flutter 2, but detected that Touchable does not have support for null safety.

    It could be good if this package upgraded to null safety in case someone want to upgrade to Flutter 2

    opened by RegisSaffi 14
  • Runable example folder and bugfix for flutter web

    Runable example folder and bugfix for flutter web

    Good package!

    I updated the example folder with a runnable struction and I fixed a bug for flutter web. The bug was about a naming clash with your "Point" class and a point in dart ui / Math. I just renamed your Point class to TPoint and then it works in flutter web.

    opened by udiedrichsen 4
  • How can I force a repaint on click; Change color on click event

    How can I force a repaint on click; Change color on click event

    Hi,

    I am trying to use this library to create this behaviour:

    I have a filled Path. When I tap on the path it should turn into red. So using this, I can catch the onTapDownevent:

    myCanvas.drawPath(path, paint, onTapDown: (tapDetail) {
      print('Tapped');
    });
    

    What do I have to do to force the repaint with a new color?

    Thanks in advance!

    question 
    opened by S-Man42 3
  • Panning and scaling inside an InteractiveViewer

    Panning and scaling inside an InteractiveViewer

    I have a CanvasTouchDetector with a CustomPainter and I want it to be zoomed, panned, etc. so I have put it inside a Flutter's InteractiveViewer.

    The touchable features (I'm using onTapDown) work fine, but you cannot pinch inside the CustomPainter in order to zoom or pan the picture.

    How would you arrange the CanvasTouchDetector inside the InteractiveViewer in order for these features to keep working?

    Regarding the evolution of touchable, I believe the TouchyCanvas would need to detect zoom/pan gestures and bubble them up?

    opened by jagomf 2
  • How to change state of shapes using touchable?

    How to change state of shapes using touchable?

    class MyPainter extends CustomPainter { 
    
      @override
      void paint(Canvas canvas, Size size) {
    var myCanvas = TouchyCanvas(context,canvas);
      myCanvas.drawRect( rect , paint , onPanDown: (tapDetail){
       print("rectangle touch");
      print(rect.width);
    paint.color=Colors.black;
    
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
      }
    
    }
    

    Changing the color by paint.color doesn't change the color of the rectangle... how would the state change ? it's only printing stuff.

    Thank you for this library.

    opened by ameeee 2
  • Hello, i interest in this repo.

    Hello, i interest in this repo.

    I watch the source code, and don't understand the role of ShapeHandler, can you explain what it does, and how it work? Thank you.

    https://github.com/nateshmbhat/touchable/blob/3e7a860c91541eaa729747e5e8084b2dc24eec74/lib/src/shape_handler.dart#L9

    question 
    opened by xrr2016 2
  • Doesn't recognize touch event when has a GestureRecognizer in child tree

    Doesn't recognize touch event when has a GestureRecognizer in child tree

    CanvasTouchDetector(
      builder: (context) => CustomPaint(
        child: SizedBox(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.width * 9 / 16,
          child: VlcPlayer(
            aspectRatio: 16 / 9,
            url: videoUri,
            controller: _playerController,
            placeholder: CircularProgressIndicator(),
          ),
        ),
        foregroundPainter: CameraControlPainter(camera, context),
      ),
    ),
    
    bug 
    opened by Gerpea 2
  • Error: Having both a Pan Gesture Recogniser and a Scale Gesture Recogniser is redundant

    Error: Having both a Pan Gesture Recogniser and a Scale Gesture Recogniser is redundant

    Since upgrading to Flutter 2.10.4, the plugin is returning an error:

    The following assertion was thrown building CanvasTouchDetector(dirty, state: _CanvasTouchDetectorState#55157):
    Incorrect GestureDetector arguments.
    
    Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan.
    
    Just use the scale gesture recognizer.
    

    My implementation uses onTapDown with an svg. Prior to upgrading Flutter, the plugin was working perfectly.

    Any solutions to this?

    opened by nialljawad96 1
  • onTapDown only for last drawn polygon

    onTapDown only for last drawn polygon

    I'm drawing multiple polygons but the event is only triggered on the last drawn one. Any clue what the issue could be?

    For each Polygon a new TouchyCanvas is added, is this a problem?

    opened by yeonsann 1
  • Circles cannot be touched in the bottom right quadrant

    Circles cannot be touched in the bottom right quadrant

    hello. circles don't appear to receive touch events on the bottom right corner.

    This is not much of an issue on mobile where the contact area is huge but with mice there are obvious issues.

    am I missing something or is this a bug?

    opened by xerotolerant 1
  • Crashes when trying to use onPanUpdte to draw new points

    Crashes when trying to use onPanUpdte to draw new points

    Hi I am trying to achieve a drawing functionality using this library. I want to draw a shape using path & then want user to draw within that area. This library have all the events to implement that but when I am trying to draw all the generated points then it crashes my app.

    myCanvas.drawPath(AlphabetPaths.getAlphabetA(size), paint,
            onPanUpdate: (details) {
          points.add(details.localPosition);
          drawNewPath(myCanvas, points);
        });
    
    drawNewPath(myCanvas, points) {
        print(points);
        points.removeWhere((value) => value == null);
        var _pointPaint = Paint()
          ..color = Colors.black
          ..strokeWidth = 5
          ..style = PaintingStyle.fill
          ..strokeCap = StrokeCap.square;
          myCanvas.drawPoints(PointMode.points, points, _pointPaint);
      }
    

    When I am trying to draw new points using onPanUpdate it always crashing on both mobile & web. If I try to add those points directly its working fine.

    I am getting following error

    I/flutter ( 8546): [Offset(123.0, 469.3), Offset(123.0, 472.4)]
    F/libc    ( 8546): Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x28 in tid 10318 (1.ui), pid 8546 (le.gesture_demo)
    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    Build fingerprint: 'OnePlus/OnePlus5/OnePlus5:10/QKQ1.191014.012/2010292059:user/release-keys'
    Revision: '0'
    ABI: 'arm64'
    Timestamp: 2021-03-30 13:56:54+0530
    pid: 8546, tid: 10318, name: 1.ui  >>> com.example.gesture_demo <<<
    uid: 11240
    signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x28
    Cause: null pointer dereference
        x0  0000000000000000  x1  000000701f108908  x2  000000701f108958  x3  0000000000000000
        x4  000000701f108960  x5  000000702153a0d0  x6  0000000000000002  x7  000000000000003c
        x8  0000000000000000  x9  0000000000000002  x10 0000000000000001  x11 0000000000000000
        x12 0000000000000005  x13 0000008000000000  x14 000a68cc29c0d93d  x15 000000701f108a10
        x16 0000007021ccb398  x17 00000071116a98b8  x18 000000701ee8a000  x19 0000000000000000
        x20 000000702153a0d0  x21 0000007021539954  x22 0000007021c70278  x23 0000007021c6e2c0
        x24 000000701030ced1  x25 000000701f030000  x26 000000700e7c3800  x27 0000007010310730
        x28 0000000000000004  x29 000000701f108a48
        sp  000000701f108900  lr  000000702153b63c  pc  000000702153a0d0
    backtrace:
          #00 pc 00000000013220d0  /data/app/com.example.gesture_demo-pJ5hsmnHb2pDpOT8vlkuyA==/lib/arm64/libflutter.so (BuildId: 4f453a1bb64d1244a9dd8bd68e926b4779043b43)
          #01 pc 00000000000f6914  <anonymous:701f012000>
    Lost connection to device.
    Exited (sigterm)
    
    

    There are no null values in the array as you can see in the first line of crash log which are the list of points. I also added code to remove if there is any null value available.

    You can see that right now points only have 2 items but its still crashing.

    I tried multiple things but not able to fix this. I don't have any idea why its crashing with only 2 points. Do you have any idea why this is happening or can you fix this? If you found the issue let me know. I might be able to help you with this.

    Thanks

    opened by kumar-aakash86 1
  • Gesture detection broken with canvas.rotate canvas.translate

    Gesture detection broken with canvas.rotate canvas.translate

    Drawing an onTapDown shape before rotation or translation works ok, however; if you rotate or translate the canvas and then draw a shape - the shape does not register onTapDown. If I do not rotate or translate the canvas, the shape is drawn in the wrong location, but onTapDown now works.

    I've tried attaching the canvas with touchyCanvas = TouchyCanvas(context, canvas) after the rotation events but this does not fix the onTapDown behaviour.

    Is there a workaround for this?

    [edit] I found the onTapDown gestures located at the side of the canvas layered on top of each other (clicking one activates them all). Ultimately, it appears the gestures are being inserted and tracked - but they are not attached to the shapes drawn using touchyCanvas that have been rotated.

    opened by morgano86 2
  • Added onHover and onTapCancel callbacks

    Added onHover and onTapCancel callbacks

    Touchable didn't provide onTapCancel and onHover callbacks, so I added them. It can be used to interact better with the canvas on web & desktop.

    An example for the onHover usage is to display a MouseRegion with a SystemMouseCursors.click when hovering clickable parts of a canvas.

    On the other end, onTapCancel allows to disable more easily a pressed state when cancelling a tap on a Shape.

    Example:

    MouseRegion(
      cursor: _cursor ?? SystemMouseCursors.basic,
      child: CanvasTouchDetector(
        gesturesToOverride: const [
          GestureType.onTapDown,
          GestureType.onTapUp,
          GestureType.onHover,
          GestureType.onTapCancel,
        ],
        builder: (context) => CustomPaint(
          painter: MyCustomPainter(
            context,
            // other args
            onHover: (details) {
              if (details.hovering == true) {
                if (_cursor != SystemMouseCursors.click) {
                  setState(() {
                    _cursor = SystemMouseCursors.click;
                  });
                }
              } else {
                if (_cursor != SystemMouseCursors.basic) {
                  setState(() {
                    _cursor = SystemMouseCursors.basic;
                  });
                }
              }
            },
            onTapDown: (_) {
              // Do some stuff
              setState(() {
                _isPressing = true;
              });
            },
            onTapUp: (_) {
              setState(() {
                _isPressing = false;
              });
            },
            onTapCancel: () {
              setState(() {
                _isPressing = false;
              });
            },
          ),
        ),
      ),
    );
    

    And in MyCustomPainter's paint method:

    var myCanvas = TouchyCanvas(context, canvas);
    myCanvas.drawRRect(
          buttonRect,
          Paint()..color = isPressing ? Colors.blue : Colors.red,
          onTapDown: onTapDown,
          onTapUp: onTapUp,
          onTapCancel: onTapCancel,
          onHover: onHover,
        );
    
    opened by apalala-dev 2
  • Incorrect Gesture Arguments

    Incorrect Gesture Arguments

    Error : "The following assertion was thrown building CanvasTouchDetector(dirty, state: _CanvasTouchDetectorState#56194): Incorrect GestureDetector arguments.

    Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan.

    Just use the scale gesture recognizer."

    I wanted to use only tap so deleting other gesture detectors solved the problem for me

    opened by Adityabhaumik 2
  • Example doesn't work out of the box

    Example doesn't work out of the box

    Steps to repro:

    1. clone this repo.
    2. run the example
    3. click on screen1 button.

    You get this error:

    Incorrect GestureDetector arguments.
    
    Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan.
    
    Just use the scale gesture recognizer.
    
    opened by mrshootingstar 2
Releases(v1.0.0)
Owner
Natesh Bhat
Yo bros ! Wanna talk some Tech ? I go by the name "Friendly Developer" on YouTube and peers call me Seeker 👨🏻‍💻 You can connect on linked in @nateshmbhat
Natesh Bhat
A flutter package with classes to help testing applications using the canvas

Canvas test helpers MockCanvas is a utility class for writing tests for canvas operations. It supports the same API as the regular Canvas class from d

Blue Fire 12 Jan 31, 2022
Renders a wavefront .obj on to a canvas.

flutter_3d_obj A flutter package to render wavefront obj files on a canvas. Usage To use this package, add flutter_3d_obj to your pubspec.yaml file. T

Hemanth Raj V 146 Nov 3, 2022
Draw perfect freehand lines—in Flutter.

Draw perfect pressure-sensitive freehand lines. ?? A port of the perfect-freehand JavaScript library. Try out that demo. ?? Love this library? Conside

Steve Ruiz 184 Dec 8, 2022
Library for help you make userbot or bot telegram and support tdlib telegram database and only support nodejs dart and google-apps-script

To-Do telegram client dart ✅️ support multi token ( bot / userbot ) ✅️ support bot and userbot ✅️ support telegram-bot-api local server ✅️ support tel

Azka Full Snack Developer:) 73 Jan 7, 2023
Manually add the extra hitTest area of a widget without changing its size or layout.

extra_hittest_area Language: README.md | 中文简体 Manually add the extra hitTest area of a widget without changing its size or layout. Parent widgets The

FlutterCandies 13 Dec 6, 2022
library to help you create database on local memory, support json local database inspired by lowdb

Licensed Licensed under the MIT License <http://opensource.org/licenses/MIT>. SPDX-License-Identifier: MIT Copyright (c) 2021 Azkadev <http://github.c

Azka Full Snack Developer:) 35 Oct 17, 2022
Official Git of flutter code-push made by Chimera inc. If you want to get more info or seek for biz corporation, you can contact [email protected].

中文版 Chimera Flutter Code Push Chimera is a Dart compiler developed by ourselves, which generates interpretable and executable bytecode to implement co

Waytoon 21 Oct 6, 2022
Chuanying - what you see is what you get. 传影--所见即所得

传影--所见即所得 简介 从前 想要制作证件照,需要先把图片用QQ传到电脑,再用PS手动抠图; 看到一句喜欢的话,想要记到电脑里,需要一个字一个字用键盘打出来; 看到黑板上老师写的公式,想要记到Word里,需要用MathType一点点打出来; 但是有没有可能,只用手机拍照,就能搞定上面所有的事,一步

null 16 Apr 8, 2022
Flutter package to help you lazily load and display pages of items as the user scrolls down your screen.

Flutter package to help you lazily load and display pages of items as the user scrolls down your screen.

Edson Bueno 425 Dec 13, 2022
A package that lets you include a cool, nice looking and validated Password TextFormField in your app to enhance user experience. The package is fully & easily modifiable.

A package that lets you include a cool, nice looking and validated Password TextFormField in your app to enhance user experience. The package is fully

Muhammad Hamza 20 Jun 7, 2022
The Dart Time Machine is a date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.

The Dart Time Machine is a date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.

null 2 Oct 8, 2021
MB Contact Form is a basic flutter widget which helps you for your contact page.

mb_contact_form is a third party flutter package. This is a simple version of Contact Form. This can be easily added to your flutter projects. This make your works simpler and your code shorter. This is recently updated and has null safety too.

Mouli Bheemaneti 2 Oct 17, 2022
A flutter package that allows you to transform your excel to json

excel_to_json A package that allows you to transform your excel to the following format: Excel To JSON Getting Started At current the package allows y

Vitor Amaral de Melo 0 Nov 7, 2022
Flutter App which lets you share memes among your friends.

meme_share_app About App Flutter App which lets you share memes among your friends . Here one have 2 options : NEXT : Load Next Meme. SHARE : To Share

null 0 Oct 30, 2021
Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.

Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.

Luke Pighetti 3.5k Jan 4, 2023
Scribble is a lightweight library for freehand drawing in Flutter supporting pressure, variable line width and more!

Scribble Scribble is a lightweight library for freehand drawing in Flutter supporting pressure, variable line width and more! A

Tim Created It. 73 Dec 16, 2022
A library for YAML manipulation with comment and whitespace preservation.

Yaml Editor A library for YAML manipulation while preserving comments. Usage A simple usage example: import 'package:yaml_edit/yaml_edit.dart'; void

Dart 17 Dec 26, 2022
A JMAP client library in Dart to make JMAP method calls and process the responses

JMAP Dart client A JMAP client library to make JMAP method calls and process the responses. We most notably use it to write the TMail Flutter applicat

LINAGORA 18 Dec 19, 2022