1

Even after setting the state of Riverpod stateNotifierProvider, the state is null when used in the widget.

I have this provider

final imageNotifierProvider =
    StateNotifierProvider.autoDispose<ImageNotifier, FilePickerResult?>(
        (ref) => ImageNotifier());

class ImageNotifier extends StateNotifier<FilePickerResult?> {
  ImageNotifier() : super(null);

  void addImage(FilePickerResult result) {
    state = result;
  }
}

I'm reading the state like this: final selectedImage = ref.watch(imageNotifierProvider);

Inside an onpressed event I'm setting the state to FilePickerResult and also printing the state.

IconButton(
  onPressed: () async {
    final image = await ref.read(externalFileFunctionsProvider).getSingleImage();
    if (image != null) {
      print("Image selected !!");
      ref.read(imageNotifierProvider.notifier).addImage(image);
      print(selectedImage);
    }
  },
  icon: Icon(Icons.add),
)

I am expecting the output after the onpress event something like:

Image selected !!
// some FilePickerResult

Instead I'm getting:

Image selected !!
null
This is the full code
final imageNotifierProvider =
    StateNotifierProvider.autoDispose<ImageNotifier, FilePickerResult?>(
        (ref) => ImageNotifier());

class ImageNotifier extends StateNotifier<FilePickerResult?> {
  ImageNotifier() : super(null);

  void addImage(FilePickerResult result) {
    state = result;
  }
}

Future<void> addNewGroupChatDialog(
  BuildContext context,
  WidgetRef ref,
  String userId,
  List<GroupChatDetail?> chats,
) {
  TextEditingController groupNameController = TextEditingController();

  final firestoreChatFunctions = ref.watch(firestoreChatProvider);
  final selectedImage = ref.watch(imageNotifierProvider);

  return showDialog<void>(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        backgroundColor: Theme.of(context).cardColor,
        title: Text(selectedImage?.files.toString() ?? ""),
        content: SingleChildScrollView(
          child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
            TextField(
              controller: groupNameController,
              style: Theme.of(context).textTheme.labelMedium,
              decoration: const InputDecoration(
                  focusedBorder: InputBorder.none,
                  hintText: "Group Name",
                  contentPadding: EdgeInsets.all(5)),
              autofocus: true,
            ),
            if (selectedImage == null)
              Stack(
                alignment: Alignment.center,
                children: [
                  CircleAvatar(
                    radius: 50,
                    backgroundImage:
                        AssetImage('assets/images/profile_placeholder.jpeg'),
                  ),
                  IconButton(
                    onPressed: () async {
                      final image = await ref
                          .read(externalFileFunctionsProvider)
                          .getSingleImage();
                      if (image != null) {
                        print("Image selected !!");
                        ref
                            .read(imageNotifierProvider.notifier)
                            .addImage(image);
                        print(selectedImage);
                      }
                    },
                    iconSize: 50,
                    color: Colors.black,
                    icon: Icon(Icons.add),
                  )
                ],
              ),
            if (selectedImage != null)
              CircleAvatar(
                radius: 50,
                backgroundImage:
                    FileImage(File(selectedImage!.files.first.path ?? "")),
              )
          ]),
        ),
        actions: <Widget>[
          TextButton(
            child:
                Text('Cancel', style: Theme.of(context).textTheme.bodyMedium),
            onPressed: () {
              Navigator.of(context).pop();
            },
          ),
          TextButton(
              child: Text('Add', style: Theme.of(context).textTheme.bodyMedium),
              onPressed: () {
                if (groupNameController.text == "") return;
                firestoreChatFunctions.addNewGroupChat(
                    result: selectedImage,
                    groupName: groupNameController.text,
                    uid: userId);
                Navigator.pop(context);
              }),
        ],
      );
    },
  );
}

SnackBar errorSnackBar(String msg) {
  return SnackBar(content: Text(msg));
}
Ayan Das
  • 9
  • 3

2 Answers2

0

The whole point is that your provider is disposed of because when the dialog is called, it is probably no longer tracked. You can verify this by adding it to your provider:

ref.onDispose((){print('Has been Disposed');});

To solve the problem:

  1. you can remove the .autoDispose modifier
  2. add this line
final selectedImage = ref.watch(imageNotifierProvider);

in the widget from which your addNewGroupChatDialog method is called. Thus, the provider will not be disposed of.

Notes:

  1. Use ref.read instead of ref.watch in callbacks (your addNewGroupChatDialog is a callback):
  final firestoreChatFunctions = ref.read(firestoreChatProvider);
  final selectedImage = ref.read(imageNotifierProvider);
  1. You created a groupNameController but did not provide a disposal method. Memory leak, hello?
TextEditingController groupNameController = TextEditingController();

// when groupNameController.dispose(); ?

You can pass your controller from the main widget (which has an overridden dispose() method, or use a statefull widget here in the dialog)

Ruble
  • 2,589
  • 3
  • 6
  • 29
  • I removed the .autodispose and replaced ref.watch with ref.read in the callback, but still the state remains null after calling ref.read(imageNotifierProvider.notifier).addImage(image), but when I close the dialog and re-open the dialog, the state has the value previously set. Really appreciate your help. – Ayan Das Apr 18 '23 at 12:14
  • I'm glad it helped you! Mark this answer as correct, please, to help others. – Ruble Apr 18 '23 at 14:55
  • It still doesn't re-builds the AlertDialog, I have to close and re-open it to see the change in state. But I found a solution, I'll post that. – Ayan Das Apr 18 '23 at 16:29
  • Your solution is correct, but your answer does not address the original question asked... – Ruble Apr 19 '23 at 06:44
  • I tried adding onDispose(), and found that the provider was not being disposed when dialog was shown, so that was not the problem. – Ayan Das Apr 21 '23 at 06:57
-1

Solved this by wrapping Dialog() with Consumer()

I don't know if this is the right way, but I solved it by wrapping the Alert dialog with a Consumer() and adding final selectedImage = ref.watch(imageNotifierProvider) under the consumer.

showDialog<void>(
    context: context,
    builder: (BuildContext context) {

    // Used a consumer

      return Consumer(
          builder: (BuildContext context, WidgetRef ref, Widget? child) {

     // Watching the provider
        final selectedImage = ref.watch(imageNotifierProvider);

        return AlertDialog(
          backgroundColor: Theme.of(context).cardColor,
          ......... .. ..
Ayan Das
  • 9
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 22 '23 at 05:51