1

After the user taps, I want the canvas to redraw, but it never does. I tried to use setState and return different painters but nothing seems to work. It gets repainted if I flip the screen or replace the page with itself but that's really inefficient and doesn't look good. I tried researching this myself but all potential answers were too confusing to understand or simply did not work. Any help would be greatly appreciated.

Here are the relevant parts of the code:

import 'package:flutter/material.dart';

class Playpage extends MaterialPageRoute<Null> {
  Playpage() : super(builder: (BuildContext ctx) {
    return Scaffold(
      appBar:PreferredSize(
        preferredSize: Size.fromHeight(40.0),
        child:AppBar(

          title: Text('Level!'),
          centerTitle: true,
        ),
      ),
      body: Container(
        color: Colors.white,
        padding: EdgeInsets.symmetric(horizontal: 50, vertical: 0),
        child: LayoutBuilder(
          // Inner yellow container
          builder: (_, constraints) => Container(
            width: constraints.widthConstraints().maxWidth,
            height: constraints.heightConstraints().maxHeight,
            color: Colors.yellow,
            child:
            GestureDetector(
              onTap: () {

                //Navigator.pushReplacement(ctx, Playpage());
              },


              onTapDown: (TapDownDetails details) => _onTapDown(details),
              child: new CustomPaint (painter: new BoardPainter()),
            ),

          ),
        ),
      ),

    );

  }
  );
}


Color colour= Colors.white;

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

  @override
  // TODO: draw something with canvas
  void paint(Canvas canvas, Size size){
        canvas.drawRRect(
          RRect.fromRectAndRadius(Rect.fromLTWH(200, 200, 100, 100), Radius.circular(20)),
          Paint() ..color = colour,
        );
  }
}

_onTapDown(TapDownDetails details) {
  //setState() {
colour = Colors.blue;
BoardPainter();
//}
}

1 Answers1

1

As stated in the documentation:

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.

One of the two recommended ways, and the easiest path, to trigger a repaint in a CustomPainter is via a Listenable.

We could do it as follows:

class Playpage extends MaterialPageRoute<Null> {
 ...

 final _counter = ValueNotifier<int>(0);
  BoardPainter painter;

  @override
  void initState() {
    super.initState();
    painter = BoardPainter(repaint: _counter);
  }

  ...
  child: CustomPaint(
         painter: painter,
  ),
  ...

  ...
  bool repaint() {
    _counter.value++;
    return true;
  }
  ...

}

class BoardPainter extends CustomPainter {
  BoardPainter({Listenable repaint}): super(repaint: repaint);
  @override
  void paint(Canvas canvas, Size size) {
    // Draw
  }
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
Stefano Amorelli
  • 4,553
  • 3
  • 14
  • 30
  • you dont need `_MyPainter painter;` field - actually if you want that field it is easier not to use `super(repaint: repaint)` but instead to use `class BoardPainter extends CustomPainter with ChangeNotifier` and implement "repaint" method inside `BoardPainter` – pskink Dec 31 '20 at 06:59
  • btw: it should be `_counter.value++` not `_counter++` – pskink Dec 31 '20 at 08:42
  • actually in my first comment i meant that you dont need any reference (class field) to your `CustomPainter` as you dont call any methods on it... ;-) you would need it however if your `CustomPainter` mixed with `ChangeNotifier` (`class BoardPainter extends CustomPainter with ChangeNotifier`) – pskink Dec 31 '20 at 09:03