flutterlazy-loadingeager-loadingriverpod

Is there a way to make eager loading with riverpod?


I have an app that consists of two screens: Home and List_of_items. Home is displayed first and has a button that routes to the List_of_items. List_of_items uses riverpod to load a list of items. And there is some delay, about half a second when the List_of_items screen is empty, caused by riverpod lazy-loading items from the source. This is how the code looks:

class Home extends StatelessWidget { ...
     Widget build(BuildContext context) {...

class ListOfItems extends ConsumerWidget {...
     Widget build(BuildContext context, WidgetRef ref) {
        List<Item> items = ref.watch(itemsProvider).items;
   

I want riverpod eager-loading items when app started.
So, I changed Home to a ConsumerWidget and load items from there without actually using them:

class Home extends ConsumerWidget { ...
     Widget build(BuildContext context, WidgetRef ref) {
        ref.watch(itemsProvider).items;

class ListOfItems extends ConsumerWidget {...
     Widget build(BuildContext context, WidgetRef ref) {
        List<Item> items = ref.watch(itemsProvider).items;

Problem solved, but is there a way to get an instance of WidgetRef outside of ConsumerWidget? Something like this:

  void main() {
    runApp(
       const ProviderScope(child: MainApp()),
       Riverpod.widgetRef().watch(itemsProvider);
   );
}

Solution

  • I have a couple of videos on warming up riverpod providers in main. The basic technique is to create a ProviderContainer, use it to "read" all the providers you want already enabled (presuming they're keepalive), and then pass that container into the ProviderScope for the final run.

    My video is at https://youtu.be/ob8M9c-6III. I've since found a more interesting technique of having a top-level consumer that waits for multiple providers in parallel, and handles AsyncErrors gracefully, but I haven't finished the video on that. The code looks like this though [EDIT: code has been replaced with the code from the new video at https://youtu.be/LEk6AWroib8]:

    class WarmUp extends ConsumerStatefulWidget {
      const WarmUp({super.key});
    
      @override
      ConsumerState<WarmUp> createState() => _WarmUpState();
    }
    
    class _WarmUpState extends ConsumerState<WarmUp> {
      bool warmedUp = false;
    
      @override
      Widget build(BuildContext context) {
        log('warmedUp: $warmedUp');
        if (warmedUp) {
          return const MyApp();
        }
    
        // the list of future and stream providers to warm up
        final providers = <ProviderListenable<AsyncValue<Object?>>>[
          prefsProvider,
          threeProvider,
          goGetterProvider(Uri.parse('https://flutter.dev')),
        ];
        // subscribe to all the providers and check their states
        final states = providers.map(ref.watch).toList();
    
        // first, handle any errors
        for (final state in states) {
          if (state is AsyncError) {
            log('warm up failed: $state');
            Error.throwWithStackTrace(state.error, state.stackTrace);
          }
        }
        // next, see if we're done
        if (states.every((state) => state is AsyncData)) {
          log('warm up is done');
          Future(() => setState(() => warmedUp = true));
        }
        // keep spinning until warmedUp is true here...
        return const FittedBox(child: CircularProgressIndicator.adaptive());
      }
    }