Search code examples
flutterdartflutter-providerflutter-riverpod

flutter_riverpod - Accessing a `Ref` when `context` is available


I'm in the process of migrating my app to use flutter_riverpod package. At the moment, my app is using provider package to manage/deal with app state.

I have a lot of static methods inside multiple utility classes that take a BuildContext as input. I use the context to access the provider, which allows me to read and modify the state:

// This relies on the provider package
abstract class SomeRandomUtilityClass {

  static FutureOr<String?> redirect(BuildContext context) {
    bool isSignedIn = context.read<AuthProvider>().isSignedIn;
  
    if (!isSignedIn) {
      return '/signIn';
    } else {
      return null;
    }
  }

How do I achieve this with Riverpod? From what I understand, you need a Ref to be able to read/write from/to a provider, but I'm not even sure if the APIs allow it.

I tried replacing the BuildContext parameters of my methods with a Ref object, but I'm having trouble getting one. All I can access is a WidgetRef:

class App extends ConsumerWidget {
  /** ... **/

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp.router(
      /** ... **/
      routerConfig: GoRouter(
        /** ... **/
        routes: [
          GoRoute(
            /** ... **/
            // This doesn't work, because the method I'm calling expects a Ref,
            // and not a WidgetRef.
            redirect: (_, __) => SomeRandomUtilityClass.redirect(ref),
          ),
        ],
      ),
    );
  }
}

An easy fix would be to modify my static methods by changing the type of the parameter from Ref to WidgetRef, but the maintainer of the Riverpod package says that passing WidgetRefs is a bad idea.

I might be misunderstanding something. Please let me know if I am.


Solution

  • As you stated, yes, WidgetRef and Ref are of two different types, thus the error.

    To understand why, consider that this: passing WidgetRef is a bad idea because WidgetRefs are (indeed) linked their Widget's element. Their life cycle are indeed tied to the life cycle of a widget, whereas the lifecycle of a provider is a different thing. In a certain way, WidgetRef is closer to an AutoDisposeRef.

    But passing around WidgetRefs around would be dangerous since you could technically mark widgets as dirty in a way that's not meant to be with Riverpod.

    Long story short, in general, there's a good chance you want to refactor static methods into a single simple provider, which is, in the end, just a function with a ref wrapper on it.

    I'd highly suggest using riverpod_annotation, too. You'd save time and you'd obtain something very readable and concise (and hassle-free):

    @riverpod
    T yourFunction(YourFunctionRef ref) {
      final someValue = ref.watch(someDependencyProvider);
    
      // logic
    
      return someComputedValue; // Type T
    }
    

    In your specific use case, though, you're trying to implement a redirect with go router and riverpod. I'd avoid creating a simple provider like that, since it would cause the root of your app to rebuild on auth change.

    That's a "deep" topic, and there are several way to do so; I've created a repo here just to show off how to work around this. It's a 3rd party riverpod examples repo.

    Check the complete_example folder, there for a full example, or search for older and simpler examples in other folders.