flutterdartriverpodnull-safety

Handling Null Safety Error When Accessing 'state' in Flutter Riverpod


I'm integrating Riverpod for state management in a Flutter application and have encountered a null safety error related to accessing the 'state' property. This issue arises within my code that aims to update an image, a feature critical for both web and mobile platforms. Here is the specific error message I received:

"The property 'state' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!')."

The error surfaces in the _pickImage method, specifically when attempting to update the state with a new image file. Below is the relevant portion of my code:




final pickedImageProvider = StateProvider<File?>((ref) => null);
final webImageProvider = StateProvider<Uint8List>((ref) => Uint8List(8));
final tapOffsetProvider = StateProvider<Offset?>((ref) => null);

class ProfileAppBar extends ConsumerWidget {
  const ProfileAppBar({Key? key}) : super(key: key);

  Future<void> _pickImage(BuildContext context) async {
    if (!kIsWeb) {
      final ImagePicker picker = ImagePicker();
      XFile? image = await picker.pickImage(source: ImageSource.gallery);
      if (image != null) {
        var selected = File(image.path);
        context.read(pickedImageProvider).state = selected;
      } else {
        print('No image has been picked ');
      }
    } else if (kIsWeb) {
      final ImagePicker picker = ImagePicker();
      XFile? image = await picker.pickImage(source: ImageSource.gallery);
      if (image != null) {
        var f = await image.readAsBytes();
        context.read(webImageProvider).state = f;
        context.read(pickedImageProvider).state = File('a');
      } else {
        print('No image has been picked ');
      }
    } else {
      print('Something went wrong');
    }
  }

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          ...   Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  Tooltip(
                    message: 'Settings',
                    child: Semantics(
                      label: 'Pomodoro timer settings',
                      enabled: true,
                      readOnly: true,
                      child: IconButton(
                        icon: const Icon(
                          Icons.settings_outlined,
                          color: Color(0xff3B3B3B),
                          size: 24,
                          semanticLabel: 'Pomodoro timer Settings',
                        ),
                        onPressed: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => const Scaffold(),
                            ),
                          );
                        },
                      ),
                    ),
                  ),
                  GestureDetector(
                    behavior: HitTestBehavior.translucent,
                    onTapDown: (details) {
                      ref.read(tapOffsetProvider).state =
                          details.globalPosition;
                    },
                    child: IconButton(
                      onPressed: () {
                        final tapOffset = ref.watch(tapOffsetProvider).state;
                        if (tapOffset != null) {
                          showMenu(
                            position: RelativeRect.fromLTRB(
                              tapOffset!.dx - 150,
                              64,
                              tapOffset.dx ?? 0,
                              0,
                            ),
                            constraints: const BoxConstraints(
                              maxWidth: 600,
                            ),...
        ),
      ),
    );
  }
}

I've attempted using conditional access operators like ?. and null assertion operators like !, but I'm unsure of the best practice in this scenario.

Your insights and advice on addressing this null safety error effectively would be greatly appreciated.


Solution

  • Your code should work fine! I see only one problem with the button:

    IconButton(
    onPressed: () {
      final tapOffset = ref.read(tapOffsetProvider); // `state` don't required
      if (tapOffset != null) {...}
    

    In callbacks it is necessary to use ref.read.


    To use your providers, just add ! if you are sure the value is not null or use ?.

    final pickedImageProvider = StateProvider<File?>((ref) => null);
    
    {
      File? pickedImage = ref.read(pickedImageProvider);
      File pickedImageNotNull = ref.read(pickedImageProvider)!;
    
      String? pickedImagePath = ref.read(pickedImageProvider)?.path;
      String pickedImagePathNotNull = ref.read(pickedImageProvider)!.path;
    }
    

    But really your problem is that you forgot to use the .notifier and are not correctly trying to access the state:

    context.read(pickedImageProvider.notifier).state = selected;
    context.read(webImageProvider.notifier).state = f;
    context.read(pickedImageProvider.notifier).state = File('a');
    
    // Or do it this way:
    context.read(pickedImageProvider.notifier).update((_) => selected);
    context.read(webImageProvider.notifier).update((_) => f);
    context.read(pickedImageProvider.notifier).update((_) => File('a'));