skip to Main Content

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);
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    Claude Ai helped me with this, but I'm wondering if there's a better way to do it?

    import 'package:firebase_auth/firebase_auth.dart';
    import 'package:google_sign_in/google_sign_in.dart';
    import 'package:riverpod_annotation/riverpod_annotation.dart';
    
    // This line is necessary for code-generation to work
    part 'firebase_auth_notifier.g.dart';
    
    @riverpod
    class FirebaseAuthNotifier extends _$FirebaseAuthNotifier {
      @override
      FutureOr<User?> build() {
        // Return the current user, or null if not signed in
        return FirebaseAuth.instance.currentUser;
      }
    
      Future<void> signInWithGoogle() async {
        state = const AsyncLoading();
    
        try {
          // Trigger the authentication flow
          final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
    
          if (googleUser == null) {
            state = const AsyncData(null);
            return;
          }
    
          // 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,
          );
    
          // Sign in to Firebase with the Google credential
          final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
    
          state = AsyncData(userCredential.user);
        } catch (e, stackTrace) {
          state = AsyncError(e, stackTrace);
        }
      }
    
      Future<void> signOut() async {
        await FirebaseAuth.instance.signOut();
        state = const AsyncData(null);
      }
    }
    

  2. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search