Search code examples
firebaseflutterdartriverpodgorouter

How to convert a Stream to a Listenable in Flutter?


I am trying to figure out how to make use of Firebase's onAuthStateChanges() stream to use as a Listenable in the refreshListenable parameter from the go_router package to redirect whenever the authState changes. In additon I am using flutter_riverpod for State Mangement.

My code looks like this so far:

I created a simple AuthService class (shrinked down to the most important parts):

abstract class BaseAuthService {
  Stream<bool> get isLoggedIn;
  Future<bool> signInWithEmailAndPassword({ required String email, required String password });
}

class AuthService implements BaseAuthService {
  final Reader _read;

  const AuthService(this._read);

  FirebaseAuth get auth => _read(firebaseAuthProvider);

  @override
  Stream<bool> get isLoggedIn => auth.authStateChanges().map((User? user) => user != null);

  @override
  Future<bool> signInWithEmailAndPassword({ required String email, required String password }) async {
    try {
      await auth.signInWithEmailAndPassword(email: email, password: password);
      return true;
    } on FirebaseAuthException catch (e) {
      ...
    } catch (e) {
      ...
    }

    return false;
  }

Next I created these providers:

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

final authServiceProvider = Provider.autoDispose<AuthService>((ref) => AuthService(ref.read));

As mentioned before, I would like to somehow listen to these authChanges and pass them to the router:

final router = GoRouter(
    refreshListenable: ???
    redirect: (GoRouterState state) {
        bool isLoggedIn = ???
        
        if (!isLoggedIn && !onAuthRoute) redirect to /signin;
    }
)

Solution

  • According to the go_router documentation, you can simply use the following method: https://gorouter.dev/redirection#refreshing-with-a-stream

    GoRouterRefreshStream(_fooBloc.stream)
    

    UPDATE: 20/9/2022

    This class is removed from go_router 5.0, and there is no alternative class provided in go_router. The reason being that this class is unrelated to routing and should not be included in go_router. To migrate existing applications that use this class, one can copy the class to their code and own the implementation directly.

    import 'dart:async';
    
    import 'package:flutter/foundation.dart';
    
    class GoRouterRefreshStream extends ChangeNotifier {
      GoRouterRefreshStream(Stream<dynamic> stream) {
        notifyListeners();
        _subscription = stream.asBroadcastStream().listen(
              (dynamic _) => notifyListeners(),
            );
      }
    
      late final StreamSubscription<dynamic> _subscription;
    
      @override
      void dispose() {
        _subscription.cancel();
        super.dispose();
      }
    }