authStateChanges only work if in debug mode

Viewed 167

I have a flutter app and everything was fine until i want to release it. I'm using firebase for auth.

I'm using:
firebase_core: ^0.7.0
firebase_auth: ^0.20.0

In debug mode or in release, my firebase auth login works fine. My problem is after that. I have a decentralized 'listener' to firebaseAuth.authStateChanges. Here is where i control my app authentication. This is my buildSelf$ function in my auth repository(where i build the auth state listener):

    ReplayConnectableStream<AuthState> buildSelf$() {
        return Rx.concat([
          Stream.value(AuthInit()),
          firebaseAuth.authStateChanges().switchMap((firebaseUser) {
            print('[AuthRepository][buildSelf][authStateChanges] firebaseUser');
            print(firebaseUser);
            if (firebaseUser == null) {
              return Stream.value(Unauthenticated());
            }
            /* continue function */
            return Authenticated(firebaseUser: firebaseUser);
          })
        ]).publishReplay();
    }

buildSelf$ is a method for my AuthRepository. And i initialize it on:

  AuthRepository._() {
    Firebase.initializeApp().then((app) {
      _firebaseAuth = FirebaseAuth.instance;
      state$ = buildSelf$();
      state$.connect();
    });
    setPackageInfo();
  }

  static final AuthRepository instance = AuthRepository._();

All this code is inside my AuthRepository. The problem is: When i'm running my app in debug mode. Every thing works fine. I can login and my app (my navigator observer uses the auth repository state$) and i'm redirected to home page. [Here a printscreen from terminal in debug mode. success authStateChanges emit But when i'm running im release mode, my login response shows in my terminal 'success' but my listener does not show state changes. Here a printscreen from terminal in release mode. authStateChanges emit only 1 time(when opening)

I'm realy lost about whats going on. I tried even to call this authStateChanges directly in my app.dart but still the same way(in release, only checking auth state once).

Solution:

  • After 3 days of headache, i finally found out my problem: My app does not initialize firebase app on root (main). So i have to initialize it every time i need to use some firebase package. I believe that every time one app was initialized, the others failed.

I move my firebase.initializeApp() to my main function and remove every other firebase initialize. Then, everything works fine. This is my main:

void main() async {
    /* ... main code ... */
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();
    runApp(App());
}
1 Answers

I did implement a project with the presented methods in release mode in my iPhone but wasn't able to reproduce the problem, the implementation I did to reproduce the project was:

class _TestFirebaseState extends State<TestFirebase> {
  bool userLogado = false;

  _loginLogout() {
    if (FirebaseAuth.instance.currentUser == null) {
      FirebaseAuth.instance.signInWithEmailAndPassword(
          email: 'teste1@teste.com', password: '123456');
    } else {
      FirebaseAuth.instance.signOut();
    }
  }

  @override
  void initState() {
    super.initState();

    final stream = Rx.concat(
      [
        Stream.value('começou'),
        FirebaseAuth.instance.authStateChanges().switchMap<String>(
          (user) {
            debugPrint('user: ' + user.toString());
            if (user == null) {
              return Stream.value('unauth');
            } else {
              return Stream.value('auth');
            }
          },
        ),
      ],
    ).publishReplay();

    stream.listen((value) {
      debugPrint('value: ' + value);
      this.setState(() {
        this.userLogado = value == 'auth';
      });
    });

    stream.connect();
    
  }

  @override
  Widget build(BuildContext context) {
    String text = "sair";
    if (!userLogado) {
      text = "entrar";
    }

    return Scaffold(
      body: Container(
        width: double.infinity,
        height: double.infinity,
        child: Center(
          child: RaisedButton(
            onPressed: _loginLogout,
            child: Text(text),
          ),
        ),
      ),
    );
  }
}

the only detail is that the main component that loads everything in the app has a future builder that awaits the firebase to init:

Widget build(BuildContext context) {
    return MaterialApp(
      home: FutureBuilder(
        // Initialize FlutterFire:
        future: Firebase.initializeApp(),
        builder: (context, snapshot) {
          // Check for errors
          if (snapshot.hasError) {
            return Scaffold(
              body: Center(
                child: Text(
                  'ocorreu um problema inicializando o aplicativo...',
                ),
              ),
            );
          }

          // Once complete, show your application
          if (snapshot.connectionState == ConnectionState.done) {
            // return LoginPage();
            // return VerifySignIn(homePage: Home());
            return TestFirebase();
          }

          // Otherwise, show something whilst waiting for initialization to complete
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        },
      ),
      debugShowCheckedModeBanner: false,
    );
}

And another detail is that the firebase configuration is implemented following the flutter tutorial for the platform used.

So in order to evaluate if the app configuration is just fine, I would implement that button app inside the same project so you can evaluate it properly.

Related