Marco
Marco

Reputation: 1686

Turning a FutureProvider into a Provider

I use FirebaseAuth for the authentication and I've defined the following provider:

final _authStateChangesProvider = StreamProvider<User>(
    (ref) => ref.watch(authServiceProvider).authStateChanges);

The database depends on the User, so I can create it only after the user has signed-in. The problem is that the database creation is async, so its provider has to be a FutureProvider.

final databaseFutureProvider = FutureProvider<Database>((ref) {
  final authStateChange = ref.watch(_authStateChangesProvider);
  final user = authStateChange.data?.value;
  if (user != null) {
    return FirestoreDatabase.create(user: user);
  }
  throw UnsupportedError("The database cannot be accessed before the sign-in");
});

This would make the code throughout the app more cumbersome. I'd like to make the databaseFutureProvider a simple Provider to make the app's code cleaner. How could it be done?

EDIT

It would be nice to have the database creation be part of the authentication process, so that the authStateChangesProvider will fire only after the database creation. Something like the following:

final authStateChangesProvider = FutureProvider<User>((ref) {
  final authStateChanged = ref.watch(_authStateChangesProvider);
  final user = authStateChanged.data?.value;
  if (user != null) {
    // await the creation of the database provider
  }
  return user;
});

In this way only the authStateChangesProvider will be a FutureProvider. But I'm pretty new to Riverpod and I'm not sure if and how I can create a provider inside another provider.

Upvotes: 0

Views: 456

Answers (1)

Marco
Marco

Reputation: 1686

I've come up with a solution that probably isn't the best, but I'll post it here so that others can benefit from it or hopefully could improve it.

I've created a new ScopedProvider.

final databaseProvider =
    ScopedProvider<Database>((ref) => throw UnimplementedError());

Then I've created a new widget that will be put on top of the widget tree that will need the sync access to the database. This widget will just display a loading view while the database is being created and then overrides the databseProvider.

class DatabaseWidgetBuilder extends ConsumerWidget {
  final Widget Function(Database) childBuilder;

  DatabaseWidgetBuilder(
      {Key key, @required Widget Function(Database) this.childBuilder})
      : super(key: key);

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final databaseAsyncValue = watch(databaseFutureProvider);
    return databaseAsyncValue.when(
      data: (database) => ProviderScope(
        overrides: [databaseProvider.overrideAs((watch) => database)],
        child: childBuilder(database),
      ),
      loading: () => Scaffold(
        body: Center(child: CircularProgressIndicator()),
      ),
      error: (_, __) => Scaffold(
        body: Center(child: Text("Database Error")),
      ),
    );
  }
}

Upvotes: 1

Related Questions