Why are Flutter CustomPainter Class Variables not Persisting

199 views Asked by At

In Flutter, is a Custom Painter and all its class variables re-constructed from scratch every time the paint method is called? -- when setState is called in the parent?? I didn't expect that, but it seems to be the case:

I ask because I have a Custom Painter that contains an object inside the paint method (a sequence of points) that is the basis of all the subsequent painting effects. But... this object takes math in the first place just to be created... and it requires the canvas dimensions as part of that creation... which is why its inside the paint method.

So... I thought... instead of calculating the same exact skeleton shape every time paint is called, I'll make it a nullable class variable and initialize it once... then just check if its null every time in paint instead of recreating it every time.

I thought this was best practice to "move calculations out of the paint method when possible."

BUT... the unexpected result is that when I check, Flutter always says my object is null (it's recreated every time anyway).

Custom Painter:

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

class StackPainter02 extends CustomPainter {
  final List<double> brightnessValues;

  Paint backgroundPaint = Paint();
  Paint segmentPaint = Paint();

  List<Offset>? myShape;

  StackPainter02({
    required this.brightnessValues,
  }) {

    backgroundPaint.color = Colors.black;
    backgroundPaint.style = PaintingStyle.fill;
    segmentPaint.style = PaintingStyle.stroke;
  }

  @override
  void paint(Canvas canvas, Size size) {
    final W = size.width;
    final H = size.height;

    segmentPaint.strokeWidth = W / 100;

    canvas.drawPaint(backgroundPaint);

    // unfortunately, we must initialize this here because we need the view dimensions
    if (myShape == null) {
      myShape = _myShapePoints(brightnessValues.length, 0.8, W, H, Offset(W/2,H/2));
    }

    for (int i = 0; i<myShape!.length; i++) {

      // bug fix... problem: using "i+1" results in index out-of-range on wrap-around
      int modifiedIndexPlusOne = i;
      if (modifiedIndexPlusOne == myShape!.length-1) {
        modifiedIndexPlusOne = 0;
      } else {
        modifiedIndexPlusOne++;
      }
      // draw from point i to point i+1
      Offset segmentStart = myShape![i];
      Offset segmentEnd = myShape![modifiedIndexPlusOne];

      double b = brightnessValues[i];
      if (b < 0) {
        b = 0; // !!!- temp debug... problem: brightness array algorithm is not perfect
      }
      int segmentAlpha = (255*b).toInt();
      segmentPaint.color = Color.fromARGB(segmentAlpha, 255, 255, 200);

      canvas.drawLine(segmentStart, segmentEnd, segmentPaint);

    }
  }

  @override
  bool shouldRepaint(covariant StackPainter02 oldDelegate) {
    //return (oldDelegate.brightnessValues[32] != brightnessValues[32]); // nevermind
    return true;
  }
  
}

Build Method in Parent:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: CustomPaint(
          painter: //MyCustomPainter(val1: val1, val2: val2),
              StackPainter02(
            brightnessValues: brightnessValues,
          ),
          size: Size.infinite,
        ),
      ),
    );
  }

PS - A ticker is used in parent and the "brightnessValues" are recalculated on every Ticker tick -> setState

0

There are 0 answers