Issue: skipLoadingOnRefresh and skipLoadingOnReload parameters not working correctly in AsyncValue.when()
Problem Description
The skipLoadingOnRefresh and skipLoadingOnReload parameters in AsyncValue.when() are not functioning as expected. When triggering a refresh operation, it also triggers a reload, causing skipLoadingOnRefresh to be ineffective.
Current Behavior
When using either of the following methods to refresh a provider:
ref.invalidateSelf()
ref.refresh(controllerProvider.future)
The refresh operation also triggers a reload, which means:
skipLoadingOnRefresh: true does not prevent the loading state from being shown during refresh
- The distinction between refresh and reload operations is not properly maintained
Expected Behavior
-
Refresh operations (via ref.invalidateSelf() or ref.refresh()) should:
- Respect the
skipLoadingOnRefresh parameter
- When
skipLoadingOnRefresh: true, show cached data instead of the loading state during refresh
- Only trigger refresh-related behavior, not reload behavior
-
Reload operations (via ref.invalidateSelf(asReload: true)) should:
- Respect the
skipLoadingOnReload parameter
- When
skipLoadingOnReload: true, show cached data instead of the loading state during reload
- Only trigger reload-related behavior, not refresh behavior
-
Clear separation: Refresh and reload should be independent operations that don't interfere with each other.
Steps to Reproduce
- Create a simple
AsyncNotifier provider:
@riverpod
class UserData extends _$UserData {
@override
Future<String> build() async {
// Simulate API call
await Future.delayed(Duration(seconds: 1));
return 'User Data';
}
Future<void> refresh() async {
ref.invalidateSelf(); // Should trigger refresh only
}
Future<void> reload() async {
ref.invalidateSelf(asReload: true); // Should trigger reload only
}
}
- Create a widget that uses
AsyncValue.when() with skipLoadingOnRefresh: true:
class UserDataWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncData = ref.watch(userDataProvider);
return asyncData.when(
skipLoadingOnRefresh: true, // Should skip loading on refresh
skipLoadingOnReload: false, // Should show loading on reload
loading: () => Text('Loading...'),
data: (data) => Text('Data: $data'),
error: (error, stack) => Text('Error: $error'),
);
}
}
- Trigger refresh operation:
// In a button or pull-to-refresh handler
ref.read(userDataProvider.notifier).refresh();
// OR
await ref.refresh(userDataProvider.future);
-
Observed Behavior:
- The loading state is shown even though
skipLoadingOnRefresh: true is set
- The refresh operation appears to also trigger reload behavior
-
Expected Behavior:
- When
skipLoadingOnRefresh: true, the cached data should remain visible during refresh
- Only the reload operation (via
ref.invalidateSelf(asReload: true)) should show loading state when skipLoadingOnReload: false
Minimal Reproducible Example
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'example.g.dart';
// Provider
@riverpod
class ExampleData extends _$ExampleData {
@override
Future<String> build() async {
await Future.delayed(Duration(seconds: 1));
return 'Initial Data';
}
Future<void> refresh() async {
ref.invalidateSelf(); // Should only trigger refresh
}
Future<void> reload() async {
ref.invalidateSelf(asReload: true); // Should only trigger reload
}
}
// Widget
class ExampleWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncData = ref.watch(exampleDataProvider);
return Column(
children: [
// Display with skipLoadingOnRefresh: true
asyncData.when(
skipLoadingOnRefresh: true, // Problem: This doesn't work
skipLoadingOnReload: false,
loading: () => Text('Loading...'), // Shows even on refresh
data: (data) => Text('Data: $data'),
error: (error, _) => Text('Error: $error'),
),
ElevatedButton(
onPressed: () {
// This should NOT show loading due to skipLoadingOnRefresh: true
ref.read(exampleDataProvider.notifier).refresh();
},
child: Text('Refresh'),
),
ElevatedButton(
onPressed: () {
// This SHOULD show loading due to skipLoadingOnReload: false
ref.read(exampleDataProvider.notifier).reload();
},
child: Text('Reload'),
),
],
);
}
}
Expected vs Actual:
- Refresh button: Should keep showing cached data (skipLoadingOnRefresh: true)
- Reload button: Should show loading state (skipLoadingOnReload: false)
- Actual: Both buttons show loading state, indicating refresh is triggering reload behavior
Environment
- Riverpod version: 3.0.3
- Flutter version: >=3.35.0
- Dart version: >=3.8.0 <4.0.0
Additional Context
This issue significantly impacts user experience in common scenarios:
- Pull-to-refresh: Users expect to see cached data while new data loads in the background
- Background updates: Periodic data refreshes should not interrupt the UI with loading states
- Different refresh strategies: The distinction between "refresh" (update existing data) and "reload" (completely reset) is important for UX
The current behavior forces developers to implement workarounds, such as:
- Manually tracking refresh state
- Using custom loading flags
- Implementing separate refresh/reload logic outside of Riverpod's built-in mechanisms
This adds unnecessary complexity and potential for bugs.
Related Code
The issue appears to be in async_value.dart around lines 243-244, where the skipLoadingOnReload and skipLoadingOnRefresh parameters are defined but may not be properly checked when determining whether to show the loading state.
Issue:
skipLoadingOnRefreshandskipLoadingOnReloadparameters not working correctly in AsyncValue.when()Problem Description
The
skipLoadingOnRefreshandskipLoadingOnReloadparameters inAsyncValue.when()are not functioning as expected. When triggering a refresh operation, it also triggers a reload, causingskipLoadingOnRefreshto be ineffective.Current Behavior
When using either of the following methods to refresh a provider:
ref.invalidateSelf()ref.refresh(controllerProvider.future)The refresh operation also triggers a reload, which means:
skipLoadingOnRefresh: truedoes not prevent the loading state from being shown during refreshExpected Behavior
Refresh operations (via
ref.invalidateSelf()orref.refresh()) should:skipLoadingOnRefreshparameterskipLoadingOnRefresh: true, show cached data instead of the loading state during refreshReload operations (via
ref.invalidateSelf(asReload: true)) should:skipLoadingOnReloadparameterskipLoadingOnReload: true, show cached data instead of the loading state during reloadClear separation: Refresh and reload should be independent operations that don't interfere with each other.
Steps to Reproduce
AsyncNotifierprovider:AsyncValue.when()withskipLoadingOnRefresh: true:Observed Behavior:
skipLoadingOnRefresh: trueis setExpected Behavior:
skipLoadingOnRefresh: true, the cached data should remain visible during refreshref.invalidateSelf(asReload: true)) should show loading state whenskipLoadingOnReload: falseMinimal Reproducible Example
Expected vs Actual:
Environment
Additional Context
This issue significantly impacts user experience in common scenarios:
The current behavior forces developers to implement workarounds, such as:
This adds unnecessary complexity and potential for bugs.
Related Code
The issue appears to be in
async_value.dartaround lines 243-244, where theskipLoadingOnReloadandskipLoadingOnRefreshparameters are defined but may not be properly checked when determining whether to show the loading state.