Flutter: What is the correct way to detect touch enter, move and exit on CustomPainter objects

守給你的承諾、 提交于 2019-12-13 15:55:37

问题


I am beginner in Flutter and I am trying to figure how I can detect touch enter, move and exit when a user runs their finger across a custom shape and/or across multiple stacked custom shapes. Something like below

Ideally I would like to get touch events when users enter/exit pixel boundaries of each custom shape, but I wanted to get it working at-least with a MBR of the shapes. Below is the code that I have. What am I doing wrong? All it seems to do is print move when touch begins within the shapes. I have tried GestureDetector too with similar results.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.deepOrange,
      ),
      home: MyHomePage(title: 'Flutter Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Stack(
              children: <Widget>[
                Listener(
                  onPointerSignal: (PointerEvent details) {
                    print("Signal yellow");
                  },
                  onPointerMove: (PointerEvent details) {
                    print("Move yellow");
                  },
                  onPointerHover: (PointerEvent details) {
                    print("Hover yellow");
                  },
                  onPointerEnter: (PointerEvent details) {
                    print("Enter yellow");
                  },
                  onPointerExit: (PointerEvent details) {
                    print("Exit yellow");
                  },
                  child: CustomPaint(
                    painter: ShapesPainter(),
                    child: Container(
                      height: 400,
                      width: 400,
                    ),
                  ),
                ),
                Listener(
                  onPointerEnter: (PointerEvent details) {
                    print("Enter red");
                  },
                  onPointerExit: (PointerEvent details) {
                    print("Exit red");
                  },
                  child: CustomPaint(
                    painter: ShapesPainter1(),
                    child: Container(
                      height: 200,
                      width: 200,
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class ShapesPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint();
    // set the color property of the paint
    paint.color = Colors.yellow;
    // center of the canvas is (x,y) => (width/2, height/2)
    var center = Offset(size.width / 2, size.height / 2);

    // draw the circle on centre of canvas having radius 75.0
    canvas.drawCircle(center, size.width / 2, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }

  @override
  bool hitTest(Offset position) {
    // TODO: implement hitTest
    return super.hitTest(position);
  }
}

class ShapesPainter1 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint();
    // set the color property of the paint
    paint.color = Colors.red;
    // center of the canvas is (x,y) => (width/2, height/2)
    var center = Offset(size.width / 2, size.height / 2);

    // draw the circle on centre of canvas having radius 75.0
    canvas.drawCircle(center, size.width / 2, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }

  @override
  bool hitTest(Offset position) {
    // TODO: implement hitTest
    return super.hitTest(position);
  }
}

回答1:


That's because you are using one Listener per CustomPainter, you should use just one Listener for all your Stack.

And if you want to know if the current touch event is inside each Circle , you could use GlobalKeys to get the RenderBox for each Circle, then you have the renderBox, and the PointerEvent, you can easily check the HitTest, check the code:

class _MyHomePageState extends State<MyHomePage> {
  GlobalKey _keyYellow = GlobalKey();
  GlobalKey _keyRed = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text("title"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Listener(
              onPointerMove: (PointerEvent details) {
                final RenderBox box = _keyRed.currentContext.findRenderObject();
                final RenderBox boxYellow =
                    _keyYellow.currentContext.findRenderObject();
                final result = BoxHitTestResult();
                Offset localRed = box.globalToLocal(details.position);
                Offset localYellow = boxYellow.globalToLocal(details.position);
                if (box.hitTest(result, position: localRed)) {
                  print("HIT...RED ");
                } else if (boxYellow.hitTest(result, position: localYellow)) {
                  print("HIT...YELLOW ");
                }
              },
              child: Stack(
                children: <Widget>[
                  CustomPaint(
                    key: _keyYellow,
                    painter: ShapesPainter(),
                    child: Container(
                      height: 400,
                      width: 400,
                    ),
                  ),
                  CustomPaint(
                    key: _keyRed,
                    painter: ShapesPainter1(),
                    child: Container(
                      height: 200,
                      width: 200,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Also I modified the hitTest method of your CustomPainters to ignore the touchs outside the circle.

class ShapesPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint();
    // set the color property of the paint
    paint.color = Colors.yellow;
    // center of the canvas is (x,y) => (width/2, height/2)
    final center = Offset(size.width / 2, size.height / 2);

    // draw the circle on centre of canvas having radius 75.0
    canvas.drawCircle(center, size.width / 2, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }

  @override
  bool hitTest(Offset position) {
    final Offset center = Offset(200, 200);
    Path path = Path();
    path.addRRect(RRect.fromRectAndRadius(
        Rect.fromCenter(center: center, width: 400, height: 400),
        Radius.circular(center.dx)));
    path.close();
    return path.contains(position);
  }
}

class ShapesPainter1 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint();
    // set the color property of the paint
    paint.color = Colors.red;
    // center of the canvas is (x,y) => (width/2, height/2)
    var center = Offset(size.width / 2, size.height / 2);

    // draw the circle on centre of canvas having radius 75.0
    canvas.drawCircle(center, size.width / 2, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }

  @override
  bool hitTest(Offset position) {
    final Offset center = Offset(100, 100);
    Path path = Path();
    path.addRRect(RRect.fromRectAndRadius(
        Rect.fromCenter(center: center, width: 200, height: 200),
        Radius.circular(center.dx)));
    path.close();
    return path.contains(position);
  }
}


来源:https://stackoverflow.com/questions/56814862/flutter-what-is-the-correct-way-to-detect-touch-enter-move-and-exit-on-customp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!