Search code examples
flutterdartriverpod

Riverpod StateNotifierProvider depend on a FutureProvider


I have a StateNotifierProvider that depends on a FutureProvider. Currently they look like below.

final catalogProvider = StateNotifierProvider<CatalogNotifier, CatalogState>((ref) {
  final network = ref.watch(networkProvider.future); // future provider

  return CatalogNotifier(network: network);
});

this makes my CatalogNotifier accept a Future<NetworkProvider> instead of NetworkProvider and requires me to do things like below.

await (await network).doGet(...)

What's the best way to avoid having to await multiple and allow CatalogNotifier to accept a bare NetworkProvider so I can write like await network.doGet(...) ?


for completeness as requested, below is the other related providers

final networkProvider = FutureProvider<Network>((ref) async {
  final cache = await ref.watch(cacheProvider.future);
  return Network(cacheManager: cache);
});
final cacheProvider = FutureProvider<CacheManager>((ref) async {
  final info = await ref.watch(packageInfoProvider.future);

  final key = 'cache-${info.buildNumber}';

  return CacheManager(Config(
    key,
    stalePeriod: const Duration(days: 30),
    maxNrOfCacheObjects: 100,
  ));

I'm sure I can take my cache provider as a future into the network provider, so it doesn't have to be a FutureProvider, but I'm interested in how to solve the issue above, since in another scenario, if I depend on say 3 or 4 FutureProviders, this may not be an option.


Solution

  • With Riverpod v2 and its code-gen features this has become much easier since you no longer have to decide the type of the provider (unless you want to).

    StateNotifier in Riverpod 2

    
    @riverpod
    Future<CatalogController> catalog(CatalogRef ref) async {
      final network = await ref.watch(networkProvider.future);
    
      return CatalogController(network: network);
    }
    
    

    Alternative approach in Riverpod 2

    Quite often you want to have a value calculated and have a way to explicitly redo that calculation from the UI, like a list from network, but with a refresh button in the UI. This can be modeled as below in Riverpod 2.

    @riverpod
    Future<CatalogState> myFeed(MyFeedRef ref) async {
      final json = await loadData('url');
    
      return CatalogState(json);
    }
    
    // and when you want to refresh this from your UI, or from another provider:
    ref.invalidate(myFeedProvider);
    
    // if you want to also get the new value in that location right after refreshing:
    final newValue = await ref.refresh(myFeedProvider);
    
    

    Riverpod 2 also has loading and error properties for the providers. You can use these to show the UI accordingly. Though if you want to show the last result from the provider while your feed is loading or in an error state, you have to model this yourself with a provider that returns a stream/BehaviorSubject, caches the last value, etc.