How to use Riverpod with CustomPainter in flutter?

I’ve been searching around the answer and didn’t find much information about how to use Riverpod with CustomPainter.

I got two problems that showed up when I used Riverpod with CustomPainter.

  1. (Fixed, please ignore.) I tried to use CustomPainter to draw lines, but CustomPainter won’t draw the line after the start point and the end point has changed. (ref.watch not updating immediately)

  2. There is a provider I need to update inside CustomPainter, which gave me an error like this: “Modifying a provider inside those life-cycles is not allowed, as it could lead to an inconsistent UI state.” (I know I shouldn’t update the value inside the build, but I’m curious about how to solve this, especially for CustomPainter or CustomMultiChildLayout.)

Consumer(
   builder: (BuildContext context, WidgetRef ref, Widget? child) {
    return CustomPaint(
      painter: EdgePainter(
        ref: ref,
      ),
    );
  },
),
class EdgePainter extends CustomPainter {
  WidgetRef ref;

  EdgePainter({
    required this.ref,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = Colors.blue;
    var node = ref.watch(connectedNodeListProvider);
    var start = ref.watch(startKeyProvider);
    var end = ref.watch(endKeyProvider);
    var cardPositions = ref.watch(cardPositionMapProvider);

    if (node.isNotEmpty) {
      for (var i in node) {
        if (cardPositions[i.$1] != Offset.infinite &&
            cardPositions[i.$2] != Offset.infinite) {
          canvas.drawLine(cardPositions[i.$1]!, cardPositions[i.$2]!, paint);
        }
      }
    }

    if (start != null && end != null) {
      if (cardPositions[start] != Offset.infinite &&
          cardPositions[end] != Offset.infinite) {
        canvas.drawLine(cardPositions[start]!, cardPositions[end]!, paint);
        node.add((start!, end!));
        // var nodeValue = ref.read(connectedNodeListProvider);
        // nodeValue.add((start, end));
        // ref.read(connectedNodeListProvider.notifier).update((state) {
        //   state = nodeValue.toList();
        //   return state;
        // });
        start = null;
        end = null;
      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

I’m new to Riverpod, so please tell me anything I did wrong here.

You should only use ref.watch inside a widget build method, consumer builder method, or the body of another provider. Move all ref.watch to the Consumer‘s builder body and pass the values as arguments to EdgePainter:

Consumer(
   builder: (BuildContext context, WidgetRef ref, Widget? child) {
    var node = ref.watch(connectedNodeListProvider);
    var start = ref.watch(startKeyProvider);
    var end = ref.watch(endKeyProvider);
    var cardPositions = ref.watch(cardPositionMapProvider);

    return CustomPaint(
      painter: EdgePainter(
        ref: ref,
        node: node,
        start: start,
        end: end,
        cardPositions: cardPositions,
      ),
    );
  },
),

Make shouldRepaint returns true if there are changes from those parameters that should trigger the paint method.

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => start != oldDelegate.start && ... // This is just an example

Leave a Comment