Search code examples
flutterriverpod

Riverpod 2.5 and Firebase


I'm currently working with Riverpod 2.5 and Google Sign-In, and I'm trying to figure out how to integrate Riverpod with it. Riverpod 2.5 recommends not using StateProvider and ChangeNotifier. Interestingly, all the examples in their documentation still use the old version of Riverpod!

What is the best practice have Riverpod and Firebase ( Google Sign in and email for example)?

import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

part 'firebase_service.g.dart';

class FirebaseAuthServices extends _FirebaseAuthServices {
  @riverpod
  Future<UserCredential> signInWithGoogle() async {
    // Trigger the authentication flow
    final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();

    // Obtain the auth details from the request
    final GoogleSignInAuthentication? googleAuth =
        await googleUser?.authentication;

    // Create a new credential
    final credential = GoogleAuthProvider.credential(
      accessToken: googleAuth?.accessToken,
      idToken: googleAuth?.idToken,
    );

    // Once signed in, return the UserCredential
    return await FirebaseAuth.instance.signInWithCredential(credential);
  }
}

Solution

  • I would recommend you not to use a notifier provider, as it is not necessary to store the current user in the state of the provider. I recommend you to create class called AuthRepository (or service depending on your implementation), in which you implement your functions.

    class AuthenticationRepository{
        AuthenticationRepository(this._firebaseAuth);
    
        final FirebaseAuth _firebaseAuth;
    
        Stream<User?> authStateChanges() {
          return _firebaseAuth.authStateChanges();
        }
    
    
        Stream<User?> userChanges() {
          return _firebaseAuth.userChanges();
        }
    
         // TODO implement your functions
    }
    

    Now you create a new provider to share this class inside your app. When creating the authRepoProvider you can either create a new provider to inject the firebase auth instance or you can just call it inside the authRepo provider.

    @Riverpod(keepAlive: true)
    FirebaseAuth firebaseAuth(FirebaseAuthRef ref) {
      return FirebaseAuth.instance;
    }
    
    @Riverpod(keepAlive: true)
    AuthenticationRepository authRepository(AuthRepositoryRef ref) {
      final auth = ref.watch(firebaseAuthProvider);
      return AuthenticationRepository(auth);
    }
    

    To listen to the currently signed in user you can create new providers to listen to authStateChanges or userChanges stream from the firebase sdk.

    @Riverpod(keepAlive: true)
    Stream<User?> authStateChange(AuthStateChangeRef ref) {
      final auth = ref.watch(authRepositoryProvider);
      return auth.authStateChanges();
    }
    
    @Riverpod(keepAlive: true)
    Stream<User?> userChanges(UserChangesRef ref) {
      final auth = ref.watch(authRepositoryProvider);
      return auth.userChanges();
    }
    

    Note that all providers are set to keep alive as they are required in the whole app lifecycle