Error when I try to access the state property with riverpod

35 views Asked by At

Please help! I'm using riverpod with the redo and undo buttons in my app, but keep getting "This expression has a type of 'void' so its value can't be used. Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void" I exhausted all my options. Find my code below(Ln 58 -71):

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

   final drawingAreaProvider =
       StateNotifierProvider.autoDispose<DrawingAreaNotifier, List<Offset>>((ref) {
     return DrawingAreaNotifier();
   });

   final redoProvider = StateProvider.autoDispose<void>((ref) {});
   final undoProvider = StateProvider.autoDispose<void>((ref) {});
   final toggleGridProvider = StateProvider.autoDispose<bool>((ref) => false);

   class DrawingAreaNotifier extends StateNotifier<List<Offset>> {
     DrawingAreaNotifier() : super([]);

     void addPoint(Offset point) {
       state = [...state, point];
     }

     void clearPoints() {
       state = [];
     }
   }

   class DrawingArea extends ConsumerWidget {
     @override
     Widget build(BuildContext context, WidgetRef ref) {
       final points = ref.watch(drawingAreaProvider);

       return GestureDetector(
         onPanStart: (details) {
           final RenderBox renderBox = context.findRenderObject() as RenderBox;
           final localPosition = renderBox.globalToLocal(details.globalPosition);
           ref.read(drawingAreaProvider.notifier).addPoint(localPosition);
         },
         onPanUpdate: (details) {
           final RenderBox renderBox = context.findRenderObject() as RenderBox;
           final localPosition = renderBox.globalToLocal(details.globalPosition);
           ref.read(drawingAreaProvider.notifier).addPoint(localPosition);
         },
         onPanEnd: (_) {
           // Do nothing on pan end
         },
         child: Stack(
           children: [
             CustomPaint(
               painter: DrawingPainter(points),
               size: Size.infinite,
             ),
             Positioned(
               top: 20,
               left: 20,
               child: Row(
                 children: [
                   IconButton(
                     icon: const Icon(Icons.undo),
                     onPressed: () {
                       ref.read(undoProvider).state;
                     },
                   ),
                   IconButton(
                     icon: Icon(Icons.redo),
                     onPressed: () {
                       ref.read(redoProvider).state;
                     },
                   ),
                   IconButton(
                     icon: Icon(Icons.grid_on),
                     onPressed: () {
                       ref.read(toggleGridProvider).state =
                           !ref.read(toggleGridProvider).state;
                     },
                   ),
                 ],
               ),
             ),
           ],
         ),
       );
     }
   }

   class DrawingPainter extends CustomPainter {
     final List<Offset> points;

     DrawingPainter(this.points);

     @override
     void paint(Canvas canvas, Size size) {
       final paint = Paint()
         ..color = Colors.black
         ..strokeWidth = 2
         ..style = PaintingStyle.stroke;

       if (points.isEmpty) return;

       final path = Path();
       path.moveTo(points.first.dx, points.first.dy);
       for (int i = 1; i < points.length; i++) {
      path.lineTo(points[i].dx, points[i].dy);
       }

       canvas.drawPath(path, paint);

       final crossPaint = Paint()
         ..color = Colors.red
         ..strokeWidth = 2;

       for (final point in points) {
         canvas.drawLine(Offset(point.dx - 5, point.dy),
             Offset(point.dx + 5, point.dy), crossPaint);
         canvas.drawLine(Offset(point.dx, point.dy - 5),
            Offset(point.dx, point.dy + 5), crossPaint);
       }
     }

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

I tried to update these providers to use the correct syntax for initializing the state.

   final redoProvider = StateProvider.autoDispose<void>((ref) => null);
   final undoProvider = StateProvider.autoDispose<void>((ref) => null);
   final toggleGridProvider = StateProvider.autoDispose<bool>((ref) => false);

Still didn't help

1

There are 1 answers

1
pcba-dev On

It is expected that your code throws an error. As indicated in the "state" property of StateNotifier it is @protected and @visibleForTesting, which implies that it can only be accessed from child classes and test.

As recommended by the Riverpod documentation, if you want to reactively get a provided value, i.e. rebuild you widgets on changes of the provider, you shall use "watch" instead of "read".

final value = ref.watch(myCustomStateProvider);

On the other hand, if you want to get the provided value "on-demand", for example within a callback function of a button, then you shall use "read".

...
onTap: () {
     final value = ref.read(myCustomStateProvider);
     // Do something with the provided value...
  },
...

Note that both "watch" and "read" can only be used to get the value/state provided by the provider.