Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to maintain Firebase Authentication after refresh with Flutter web?

I am using the authStateChanges stream from Firebase with flutter. I have two views, one for mobile and the another one for a web application. I want to redirect the user to the SignIn screen if he is not connected, logged in or authenticated. At first it works well but then when i am logged in and refresh the browser i got the SignIn screen loaded for like 1 second and then the Web screen appears again. I checked with print what's going on and from what i saw, the authStateChanges Stream is null for that 1-2 seconds(when SignIn screen appears) and then has a value when the stream receives the connected user. Is there a way to check, or wait until this authentication is done before loading the SignIn screen when it must not load it ?

My main component contains the StreamBuilder as following:

  Widget build(BuildContext context) {
    final firebaseAuthService = Provider.of<FirebaseAuthService>(context);
    return StreamBuilder<User>(
        stream: firebaseAuthService.authStateChanges(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.active) {
            User user = snapshot.data;
            if (user == null) {
              //first time no connection
              return SignIn();
            }
            if (kIsWeb) {
              return WebMain(user: user);
            }
            // load mobile version
            return MobileMain();
          }
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );

        });
  }

Here you can find my FirebaseAuth wrapper class which contains the methods from firebase:

class FirebaseAuthService {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  User _user;
  bool get isAuthenticated {
    return _user == null ? false : true;
  }
  User get user {
    return _user;
  }
  Future<User> signInWithEmailAndPassword(
      String userEmail, String userPassword) async {
    return _user = await _firebaseAuth
        .signInWithEmailAndPassword(email: userEmail, password: userPassword)
        .then((userCredential) => userCredential.user);
  }
  Stream<User> authStateChanges() {
    _user = _firebaseAuth.currentUser;
    return _firebaseAuth.authStateChanges();
  }
  Future<void> signOut() async {
    return _firebaseAuth.signOut();
  }
}

like image 803
Arton Hoxha Avatar asked Oct 14 '22 23:10

Arton Hoxha


1 Answers

While I am not sure why authStateChanges does not notify when the user sign in state is changed (usually a second later), a similar function does seem to work for your use case.

Try idTokenChanges()

  FirebaseAuth.instance.idTokenChanges().listen((event) {
    print("On Data: ${event}");
  });

This event will return your Firebase User object. When refreshed, it might return 'null' initially, but within a second, returns your signed in User. You could potentially make the sign in page wait a couple of seconds to make sure a signed in user isn't being initialized.

EDIT: While there may be better solutions, this is currently working for me.

  final subscription = FirebaseAuth.instance.idTokenChanges().listen(null);
  subscription.onData((event) async {

    if(event != null) {
      print("We have a user now");
      isLoading = false;
      print(FirebaseAuth.instance.currentUser);
      subscription.cancel();

      Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => OverviewController())
      );

    } else {
      print("No user yet..");
      await Future.delayed(Duration(seconds: 2));
      if(isLoading) {
        Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => LoginController())
        );

        isLoading = false;
        subscription.cancel();
      }
    }
  });
like image 138
FriedRyan Avatar answered Dec 05 '22 20:12

FriedRyan