skip to Main Content

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));
}

2

Answers


  1. Chosen as BEST ANSWER

    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,
              ......... .. ..
    

  2. 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)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search