6

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

1

3 Answers 3

4

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();
      }
    }
  });
Sign up to request clarification or add additional context in comments.

2 Comments

Basically, replacing .authStateChanges() with .idTokenChanges() seems to be a workaround for the bug
Thanks for your answer i will try it when i have time
0

For me, the below code seems to work fine. Although there is a warning in docs that says "You should not use this getter to determine the user's current state, instead use [authStateChanges], [idTokenChanges] or [userChanges] to subscribe to updates."

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Diary Book',
      theme: ThemeData(
        visualDensity: VisualDensity.adaptivePlatformDensity,
        primarySwatch: Colors.green,
      ),
      home: (FirebaseAuth.instance.currentUser == null)
          ? LoginPage()
          : MainPage(),
    );
  }
}
   

I haven't encountered any issues using the above code. I Will let you know if do. If someone can comment any future errors this may have that would be great

Comments

0
FirebaseAuth.instance.authStateChanges().listen(
(event) {
  if (event == null) {
    print('----user is currently signed out');
  } else {
    print('----user is signed in ');
  }
  runApp(
      const MyApp()  
      );
    },
  );

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.