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
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.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:
To solve the problem:
.autoDispose
modifierin the widget from which your
addNewGroupChatDialog
method is called. Thus, the provider will not be disposed of.Notes:
ref.read
instead ofref.watch
in callbacks (youraddNewGroupChatDialog
is a callback):groupNameController
but did not provide a disposal method. Memory leak, hello?You can pass your controller from the main widget (which has an overridden
dispose()
method, or use a statefull widget here in the dialog)