Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw on a CustomPaint widget at position of the pointer down event?

Tags:

flutter

I'm trying to create a simple widget so that when the user presses the screen, a circle appears at that position. I have a CustomPaint widget wrapped by a Listener widget like this:

              new Listener(
                onPointerDown: down,
                child: new CustomPaint(
                  painter: painter,
                  size: Size.infinite,
                ),
              )

The problem is that the pointer down events are supplied in global coordinates, and the painting is done in coordinates local to the CustomPaint widget. How should I convert these two coordinate systems?

This page says I can use the RenderBox.globalToLocal method but then how do I get the RenderBox of the CustomPaint widget?

like image 244
Mark Avatar asked Oct 05 '17 15:10

Mark


2 Answers

You don't necessarily have to wrap the listener in a widget. You can also use a GlobalKey to get the RenderObject.

import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  @override
  State createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  GlobalKey _paintKey = new GlobalKey();
  Offset _offset;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('CustomPaint example'),
      ),
      body: new Listener(
        onPointerDown: (PointerDownEvent event) {
          RenderBox referenceBox = _paintKey.currentContext.findRenderObject();
          Offset offset = referenceBox.globalToLocal(event.position);
          setState(() {
            _offset = offset;
          });
        },
        child: new CustomPaint(
          key: _paintKey,
          painter: new MyCustomPainter(_offset),
          child: new ConstrainedBox(
            constraints: new BoxConstraints.expand(),
          ),
        ),
      ),
    );
  }

}

class MyCustomPainter extends CustomPainter {
  final Offset _offset;
  MyCustomPainter(this._offset);

  @override
  void paint(Canvas canvas, Size size) {
    if (_offset == null) return;
    canvas.drawCircle(_offset, 10.0, new Paint()..color = Colors.blue);
  }

  @override
  bool shouldRepaint(MyCustomPainter other) => other._offset != _offset;
}
like image 133
Collin Jackson Avatar answered Sep 20 '22 13:09

Collin Jackson


OK this worked for me (thanks to help from @mikemimik on gitter):

Wrap the listener in a new custom widget that extends StatelessWidget. The build() method of that widget then gets access to the RenderBox like this:

  @override
  Widget build(BuildContext context) {
    void down(PointerDownEvent evt) {
      RenderBox box = context.findRenderObject();
      painter.addPos(box.globalToLocal(evt.position));
    }

    return new Listener(
      onPointerDown: down,
      child: new CustomPaint(
        painter: painter,
        size: Size.infinite,
      ),
    );
  }
like image 36
Mark Avatar answered Sep 22 '22 13:09

Mark