aidanmack
aidanmack

Reputation: 548

Riverpod - Create service with async dependency better/elegant way

I've writen some code that provides a ApiService to a StateNotifier. The ApiService has a dependency on a authenticatorclient - The auth client has to be created asynchronously as it uses sharedprefs to get a token.

Im just trying to figure out if theirs a more elegant way to how I've written this. Basically when the service apiService is injected into the StateNotifier it could be nullable... That to me is a bit of a code smell.

So in brief this is what im doing... use a FutureProvider to Instantiate the RestClientwith a Dio

authenticatorClient = FutureProvider<RestClient>((ref) async {
  final prefs = await SharedPreferences.getInstance();
  final dio = Dio();
  ...
  return RestClient(dio);
}

And then I watch that and use a MaybeWhen to return the service

final clientCreatorWatchProvider = Provider<ApiService?>((ref) => ref
    .watch(authenticatorClient)
    .whenData((value) => ApiService(value))
    .maybeWhen(
      data: (service) => service,
      orElse: () => null,
    ));

So the bit I dont like is the orElse returning null

And then my StateNotifier is watching...

final AppState = StateNotifierProvider<AppNotifier, String>(
    (ref) => AppNotifier(ref.watch(clientCreatorWatchProvider)));

class AppNotifier extends StateNotifier<String> {
  final ApiService? apiService;

  AppNotifier(this.apiService) : super("loading") {
    init();
  }
...
}

Any thoughts on the above approach?

Thanks

Upvotes: 6

Views: 3194

Answers (1)

Alex Hartford
Alex Hartford

Reputation: 5970

One way to solve this problem is to initialize SharedPreferences outside of a provider. You can then use ProviderScope to override a synchronous provider, eliminating the need to work with AsyncValue.

When you initialize your app, do the following:

final sharedPreferences = Provider<SharedPreferences>((_) => throw UnimplementedError());

Future<void> main() async {
  final sharedPrefs = await SharedPreferences.getInstance();

  runApp(
    ProviderScope(
      overrides: [
        sharedPreferences.overrideWithValue(sharedPrefs),
      ],
      child: MyApp(),
    ),
  );
}

Now you could write your providers like so:

final authenticatorClient = Provider<RestClient>((ref) {
  final prefs = ref.watch(sharedPreferences);
  final dio = Dio();
  ...
  return RestClient(dio);
}

final clientCreatorWatchProvider = Provider<ApiService>((ref) {
  final authClient = ref.watch(authenticatorClient);
  return ApiService(authClient);
});

Upvotes: 24

Related Questions