2

I have a firebase_providers.dart file which looks like this:

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:firebase_auth/firebase_auth.dart';

final firebaseAuthProvider = Provider((ref) => FirebaseAuth.instance);

final authStateChangesProvider = StreamProvider.autoDispose<User?>((ref) {
  return ref.watch(firebaseAuthProvider).authStateChanges();
});

It creates 2 simple providers - one for Firebase Auth instance and another for fetching current user status. I am using it on my main page like this:

class NavigationManager extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final user = useProvider(authStateChangesProvider);
    return user.when(
      loading: () => CircularProgressIndicator(),
      error: (error, stack) => Text('Error occured'),
      data: (user) {
        if (user == null) return LoginPage();
        return Dashboard();
      },
    );
  }
}

This is my basic setup for changing pages based on user auth status.

LoginPage is a stateful widget which contains the text fields, controllers etc.. In this file, I have the user email and password in userEmail and userPassword, now how to use it to log in a user? Official docs for Firebase Auth uses this code:

UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
    email: "[email protected]",
    password: "SuperSecretPassword!"
);

I can do it like this too but that wouldn't involve Riverpod. I need access to firebaseAuthProvider which contains FirebaseAuth.instance. To get access to this provider, I need to be inside a HookWidget to use useProvider but that would mean cluttering ui with logic. I would like to keep the logic separate in the same file where providers exist, i.e firebase_providers.dart

I also thought about doing something like this - converting LoginPage stateful widget to HookWidget and then getting access to firebaseAuthProvider and using it to call the sign in function which can reside in firebase_providers.dart but then how would I manage the form and text fields for which I created stateful widget in the first place?

So what is the way to manage and call all these auth functions like sign in, register etc.. using Riverpod?

1 Answer 1

3

The standard approach is to create a service class that accepts a ScopedReader as a parameter.

For example:

class AuthService {
  const AuthService(this._read);

  final Reader _read;

  static final provider = Provider((ref) => AuthService(ref.read));

  Future<void> emailSignIn(String email, String password) async {
    final auth = _read(firebaseAuthProvider);
    final credential = await auth.signInWithEmailAndPassword(email: email, password: password);
    // etc.
  }
}

Then in your LoginPage:

class LoginPage extends HookWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async => context.read(AuthService.provider).emailSignIn(
            '[email protected]',
            'SuperSecretPassword!',
          ),
      child: Text('Login'),
    );
  }
}

This same pattern can be applied to repository classes using Firestore or other data providers.

2
  • What does ref.read in AuthService means?
    – gegobyte
    Commented Apr 28, 2021 at 14:14
  • ref.read behaves like ref.watch except it doesn't observe rebuilds when used to read a provider. It's a ScopedReader which is just like context.read. Passing it as a parameter to AuthService allows you to read providers without context. Commented Apr 28, 2021 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.