I have created a parent provider which is dependent on multiple providers.
@riverpod
class ParentHeader extends _$ParentHeader {
@override
FutureOr<bool> build(HeaderViewModel viewModel) async {
return await parentHeader(viewModel);
}
Future<bool> parentHeader(HeaderViewModel headerVM) async {
Trial? trial =
await ref.watch(trialProvider.future);
if (trial != null) {
Future tasks =
ref.watch(tasksProvider(headerVM.strStartDate, headerVM.strEndDate).future);
Future video = ref
.watch(videoProvider(headerVM.tmpStartDate, headerVM.tmpEndDate).future);
Future calendar = ref.watch(
calendarProvider(headerVM.strStartDate, headerVM.strEndDate).future);
var listOfResponses =
await Future.wait([tasks, calendar, video]);
return Future.value(true);
}
}
On an event, I want to invalidate only the calendarProvider
. When I try to do so all the Future.wait
3 providers(API's) get called again. But the trialProvider
is not called.
Example of a provider I am using
@riverpod
class Calendar extends _$Calendar {
@override
FutureOr<List<CalendarModel>> build(String startDate, String endDate) async {
List<CalendarModel> model = await calendarEvents(startDate, endDate);
return model;
}
Future<List<CalendarModel>> calendarEvents(String startDate, String endDate) async {
EndPoint endPoint = EndPoint();
endPoint.parameters = {"from": startDate, "to": endDate};
try {
NetworkResponse? response = await _Api().fetch(endPoint);
return response.when(success: (response) {
List<CalendarModel> events = [];
var responseMessage =
CalendarModelResponse.fromJson(response.data);
events = responseMessage.message;
return Future.value(events);
},
error: (error) {
debugPrint(" error - ${error.message}");
});
}
}
}
I am not sure what I am doing wrong. Or is there any other way to achieve this?
I have created a parent provider which is dependent on multiple providers.
@riverpod
class ParentHeader extends _$ParentHeader {
@override
FutureOr<bool> build(HeaderViewModel viewModel) async {
return await parentHeader(viewModel);
}
Future<bool> parentHeader(HeaderViewModel headerVM) async {
Trial? trial =
await ref.watch(trialProvider.future);
if (trial != null) {
Future tasks =
ref.watch(tasksProvider(headerVM.strStartDate, headerVM.strEndDate).future);
Future video = ref
.watch(videoProvider(headerVM.tmpStartDate, headerVM.tmpEndDate).future);
Future calendar = ref.watch(
calendarProvider(headerVM.strStartDate, headerVM.strEndDate).future);
var listOfResponses =
await Future.wait([tasks, calendar, video]);
return Future.value(true);
}
}
On an event, I want to invalidate only the calendarProvider
. When I try to do so all the Future.wait
3 providers(API's) get called again. But the trialProvider
is not called.
Example of a provider I am using
@riverpod
class Calendar extends _$Calendar {
@override
FutureOr<List<CalendarModel>> build(String startDate, String endDate) async {
List<CalendarModel> model = await calendarEvents(startDate, endDate);
return model;
}
Future<List<CalendarModel>> calendarEvents(String startDate, String endDate) async {
EndPoint endPoint = EndPoint();
endPoint.parameters = {"from": startDate, "to": endDate};
try {
NetworkResponse? response = await _Api().fetch(endPoint);
return response.when(success: (response) {
List<CalendarModel> events = [];
var responseMessage =
CalendarModelResponse.fromJson(response.data);
events = responseMessage.message;
return Future.value(events);
},
error: (error) {
debugPrint(" error - ${error.message}");
});
}
}
}
I am not sure what I am doing wrong. Or is there any other way to achieve this?
Share asked Mar 7 at 11:46 hemanthemant 1,8134 gold badges33 silver badges45 bronze badges 4 |2 Answers
Reset to default 0Calling await ref.watch(trialProvider.future);
is adding an async gap. During that gap Riverpod is disposing the tasks, video, and calendar providers because they don't have any subscribers. After the async gap completes and each of those providers are passed to ref.watch
, Riverpod recreates them and reruns the API calls or whatever other logic you have written.
The simplest way around this is to execute all your .watch
code at the start of parentHeader
before any async gaps:
Future<bool> parentHeader(HeaderViewModel headerVM) async {
final tasks = ref.watch(
tasksProvider(headerVM.strStartDate, headerVM.strEndDate).future,
);
final video = ref.watch(
videoProvider(headerVM.strStartDate, headerVM.strEndDate).future,
);
final calendar = ref.watch(
calendarProvider(headerVM.strStartDate, headerVM.strEndDate).future,
);
final trial = await ref.watch(trialProvider.future);
if (trial != null) {
var listOfResponses = await Future.wait([tasks, calendar, video]);
return true;
}
// Other logic here
return false;
}
This will prevent the providers from disposing but changes the provider behavior slightly: parentHeaderProvider
will now always depend on tasksProvider
, videoProvider
, and calendarProvider
, even if trialProvider
returns null
. This means those providers will be kept alive as long as parentHeaderProvider
is, and any one of them updating will cause parentHeaderProvider
to rebuild. Depending on your application maybe that's fine, and Riverpod's caching logic means it shouldn't incur too much overhead.
If you do need to avoid unnecessarily listening to those providers, you can (ab)use ref.listen
to temporarily keep alive only already initialized providers during the async gap:
Future<bool> parentHeader(HeaderViewModel headerVM) async {
// Listen to all potential dependencies that are already initialized to
// ensure they are not disposed during an async gap.
final tProvider = tasksProvider(headerVM.strStartDate, headerVM.strEndDate);
final tSub = _listenIfInitialized(ref, tProvider);
final vProvider = videoProvider(headerVM.strStartDate, headerVM.strEndDate);
final vSub = _listenIfInitialized(ref, vProvider);
final cProvider = calendarProvider(headerVM.strStartDate, headerVM.strEndDate);
final cSub = _listenIfInitialized(ref, cProvider);
// Enter async gap.
final trial = await ref.watch(trialProvider.future);
// Past async gap, close any listeners we created.
tSub?.close();
vSub?.close();
cSub?.close();
if (trial != null) {
final tasks = ref.watch(tProvider.future);
final video = ref.watch(vProvider.future);
final calendar = ref.watch(cProvider.future);
var listOfResponses = await Future.wait([tasks, calendar, video]);
return true;
}
// Other logic here
return false;
}
ProviderSubscription? _listenIfInitialized(Ref ref, ProviderBase provider) {
if (!ref.exists(provider)) return null;
final handle = ref.listen(provider, (_, _) {});
return handle;
}
In Riverpod, when you invalidate a provider using ref.invalidate
, it marks that provider's state as needing to be recomputed. If other providers depend on the invalidated provider, they will also be recomputed to ensure they have the latest data. This cascading effect can lead to multiple providers being refreshed, even if only one was explicitly invalidated.
So In your scenario, you have a ParentHeader
provider that depends on trialProvider
, tasksProvider
, videoProvider
, and calendarProvider
. When you invalidate calendarProvider
, Riverpod recognizes that ParentHeader
relies on calendarProvider
and therefore invalidates ParentHeader
as well. Consequently, all providers that ParentHeader
depends on (tasksProvider
, videoProvider
, and calendarProvider
) are recomputed. However, trialProvider
is not recomputed because its result is already cached and hasn't been marked for invalidation.
What you can try :
- Instead of watching all providers within ParentHeader
, watch only those that are necessary for its functionality.
- Manually control the invalidation process by selectively invalidating only the providers that truly need to be refreshed, thereby reducing unnecessary recomputations.
- Implementing the autoDispose
modifier on providers can help manage their lifecycle more effectively.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744931936a4601792.html
ref.invalidate
but that call does not appear in any of the given code. It's hard to say what you're doing wrong when you aren't showing what you're doing. – Abion47 Commented Mar 11 at 23:39.autoDispose
with the provider which is not being invalidated. – Munsif Ali Commented Mar 17 at 5:02