Guilherme
Guilherme

Reputation: 456

Flutter CustomPaint is not repainting

I can't make the custom painter repaint. I tried to use Listenable, callbacks, setState and nothing repaints the screen.

The docs says this:

The most efficient way to trigger a repaint is to either:

  • Extend this class and supply a repaint argument to the constructor of the CustomPainter, where that object notifies its listeners when it is time to repaint.
  • Extend Listenable (e.g. via ChangeNotifier) and implement CustomPainter, so that the object itself provides the notifications directly. In either case, the CustomPaint widget or RenderCustomPaint render object will listen to the Listenable and repaint whenever the animation ticks, avoiding both the build and layout phases of the pipeline.

But the code don't work as intended.

This is the code I'm using:

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

main() {
  runApp(MaterialApp(
    home: Scaffold(appBar: AppBar(), body: Pad()),
  ));
}

class Pad extends StatefulWidget {
  @override
  _PadState createState() => _PadState();
}

class _PadState extends State<Pad> {
  @override
  Widget build(BuildContext context) {
    final painter = Painter();
    return GestureDetector(
        onTap: () {
          setState(() {
            painter.addY(10);
          });
        },
        child: CustomPaint(
          painter: painter,
          child: Container(),
        ));
  }
}

class Painter extends CustomPainter {
  double y = 10;

  addY(val) {
    y += val;
  }

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawCircle(Offset(size.width / 2, y++), 100, Paint());
  }

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

Upvotes: 4

Views: 5294

Answers (3)

mackerel
mackerel

Reputation: 141

For "Đức Hải Trần" code, just remove SizedBox() and change shouldRepaint return value to 'true', then it works fine.

Upvotes: 0

Đức Hải Trần
Đức Hải Trần

Reputation: 135

I don't know why but when I add an empty DropdownButtonFormField widget, It worked.

Even shouldRepaint function always return false. The paint function always re-call when tap to GestureDetector.

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

main() {
  runApp(MaterialApp(
    home: Scaffold(appBar: AppBar(), body: const Pad()),
  ));
}

class Pad extends StatefulWidget {
  const Pad({Key? key}) : super(key: key);

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

class _PadState extends State<Pad> {
  @override
  Widget build(BuildContext context) {
    final painter = Painter();
    return Stack(
      children: [
        SizedBox(
          width: 0,
          height: 0,
          child: DropdownButtonFormField<String>(
              onChanged: (value) {}, items: const []),
        ),
        GestureDetector(
            onTap: () {
              setState(() {
                painter.addY(10);
              });
            },
            child: CustomPaint(
              painter: Painter(),
              child: Container(),
            )),
      ],
    );
  }
}

class Painter extends CustomPainter {
  double y = 10;

  addY(val) {
    y += val;
  }

  @override
  void paint(Canvas canvas, Size size) {
    debugPrint('paint');
    canvas.drawCircle(Offset(size.width / 2, y++), 100, Paint());
  }

  @override
  bool shouldRepaint(Painter oldDelegate) {
    return false;
  }
}

Upvotes: 1

pskink
pskink

Reputation: 24720

Listenable works just fine, use ValueNotifier for example, see https://github.com/pskink/matrix_gesture_detector/blob/master/example/lib/custom_painter_demo.dart for some sample code

Upvotes: 6

Related Questions