-
-
Notifications
You must be signed in to change notification settings - Fork 971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to Ensure FutureProvider computes and returns the initial value when I await, even if invalidated #3905
Comments
Do final sub = widgetRef.listenManual(p, (a,b){});
try {
await sub.read();
} finally
sub.close();
} |
final sub = ref.listenManual(highQualityPreviewPathProvider.future,
(previous, next) {
print("highQualityPreviewPathProvider change!");
});
try {
var highQualityPreviewPath = await sub.read();
} finally {
sub.close();
} @rrousselGit this did not work if I use a debugger here is what I can observe. ref.listenManual is called while the correct now the user can change the I don't see any call for the the new value of highQualityPreviewPathProvider has been computed. any idea what is going on? I think |
Sorry, I misunderstood your question. I thought you asked something else What you're asking is not possible. The "initial/current value", as you call it, was discarded. From the moment the provider got invalidated, any previous work is no-longer revelant and Riverpod will stop using it. |
@rrousselGit no worries. Because I await the value before it is invalidated, can I ask Riverpod at this moment to give me the actual future of that value so when Riverpod discarded it I'm still awaiting the correct value? Because I'm interested in reading the "current value" not the last value in this specific case. Let me give you some context, you might think of a better solution. |
As I said, there's no such thing. In any case, an export is a side-effect. You probably shouldn't be obtaining the value from reading the provider, but instead by using a Notfiier and calling a method on it. |
Hi @rrousselGit I followed your recommendation to move the export functionality the the notifier to have more control, but I didn't find a way to make it work. Here is my code: class HighQualityPreviewPathNotifer extends AsyncNotifier<String?> {
@override
FutureOr<String?> build() async {
...
return outputFile.path;
}
Future<void> export(String pathOutput) async {
String? highQualityPreviewPath = await future;
....
} We can see in the code the following:
if the |
What's wrong here? You can use |
If I use “future” and the user modifies a notifier what my notifier watches before the future is returned, the returned value would be the value my users just updated, not the “current value computed” when I started “await” that future. Here if my user exports an image then update the imported image while it is exporting. The exported image will use the last imported image, not the one selected at the start of the export. How can I have “futureThaDontChangeIfTheUserUpdateSomething”? |
I've already said before that what you're asking is not doable and that you should be using a different approach. Why does that distinction matters? What are you trying to achieve exactly? |
@rrousselGit, I misunderstood you. Why did you advise me to move the code inside the notifier rather than having it in an onTap and do "provider.future"? I did not observe a difference in behavior. I thought I would have more tools to do what I wanted inside the provider. I tried to give you some context here: #3905 (comment). How can I make it clearer to you what the issue is? let's take a simple app, you can select a photo to see it in black and white. Do you have a different approach in mind? Don't you think it would be useful to introduce something similar to |
No. In your scenario, that "black and white version" never truly existed. Your provider was told to cancel its work on the b/w version to start a new one. If anything, your issue is that you cancelled your b/w conversion, but didn't mean to cancel it. Sounds like you're using one provider for multiple states. You likely should split the "current image being transformed" from "an image is transformed with x parameters". This is where You could have: final imageProvider = FutureProvider.family<Image, ({String path, bool asBlackAndWhite})>((ref, options) async {
// TODO load image at path, optionally transforming it to b/w if the flag is true.
}); Given the same arguments, the image obtained will be the same. And you could then transform this FutureProvider into an AsyncNotifierProvider (while still using final imageProvider = AsyncNotifierProvider.autoDsipose.family<ImageNotifier, Image, ({String path, bool asBlackAndWhite})>(ImageNotifier.new);
class ImageNotifier extends AutoDisposeFamilyAsyncNotifier<Image, ({String path, bool asBlackAndWhite})> {
@override
Future<Image> build(({String path, bool asBlackAndWhite}) op) {
// TODO load image at path, optionally transforming it to b/w if the flag is true.
}
Future<void> export() {
final image = await future;
// TODO publish the image
}
} Changing the path/transformation in the UI would have no impact here. |
Success:@rrousselGit I was able to make it work using Question:
Is it possible to detest that our job and been cancel inside the build function or member function inside our notifer to avoid useless work and network request? How to make my provider react to the cancel event? Improvements:I would like to point out a few things so you can make riverpod more user-friendly: the documenation about familly do not memtion
maybe you should add this in "Some common use-cases for this syntax is a bit strange compare to the rest of the package: final imageProvider = AsyncNotifierProvider.autoDsipose.family<ImageNotifier, Image, ({String path, bool asBlackAndWhite})>(ImageNotifier.new); I would have expected: final imageProvider = AsyncNotifierProvider.autoDsipose.family<ImageNotifier, Image, ({String path, bool asBlackAndWhite})>(arg) {return ImageNotifier(arg);} |
Ref.onDispose
All providers have access to
That just adds unnecessary work when creating a Notifier. |
All providers have access to the family modifier, so listing all possible providers explicitly would add little value. That said, I understand how someone might feel uncertain about how to use certain combinations without clear examples. Documentation Examples:Looking at the examples in the documentation, we see the family modifier used consistently across different provider types: final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
return dio.get('http://my_api.dev/messages/$id');
}); final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
return fetchCharacters(filter: filter);
}); final exampleProvider = Provider.autoDispose.family<Something, MyParameter>((ref, myParameter) {
print(myParameter.userId);
print(myParameter.locale);
// Do something with userId/locale
}); The shape of these examples is consistent and intuitive. However, regarding Here’s what the definition looks like: final imageProvider = AsyncNotifierProvider.autoDispose.family<ImageNotifier, Image, ({String path, bool asBlackAndWhite})>(
ImageNotifier.new,
); This is what I needed to understand how to use On the Use of
|
If the user changes the
highQualityPreviewPathProvider
value while we await the computed value, this will return the user's last value.But I want in this particular case to await the current value of
highQualityPreviewPathProvider
. If the user invalidateshighQualityPreviewPathProvider
, I still want the original value returned. How can I do that with Riverport and Dart?The text was updated successfully, but these errors were encountered: