Ahmed Morra
Ahmed Morra

Reputation: 139

Flutter firebase auth doesn't persist after app is killed

I am trying to persist the firebase auth state in a flutter app by using this code from the documentation but when I kill the app in the emulator and open it again it doesn't recognize a user. I can use sharedpreferences but I want to use only firebase, what am I doing wrong here?

main.dart

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

class MyApp extends StatelessWidget {
  // Create the initialization Future outside of `build`:
  final Future<FirebaseApp> _initialization = Firebase.initializeApp();
  final FirebaseAuth auth = FirebaseAuth.instance;

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      // Initialize FlutterFire:
      future: _initialization,
      builder: (context, snapshot) {
        // Check for errors
        if (snapshot.hasError) {
          return (MaterialApp(
            home: Warning(
              warning: 'Error',
            ),
          ));
        }
        // once complete show your app
        if (snapshot.connectionState == ConnectionState.done) {
          print('CONNECTED');

          if (AuthService().user() == null) {
            return MaterialApp(
              home: LoginPage(),
            );
          } else {
            return MaterialApp(
              home: HomePage(),
            );
          }
        }
        // if nothing happens return loading
        return MaterialApp(
          home: //LoginPage()
              Warning(
            warning: 'Loading',
          ),
        );
      },
    );
  }
}

AuthService class

import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // auth change user stream
  User user() {
    // ignore: deprecated_member_use
    _auth.authStateChanges().listen((User user) {
      if (user == null) {
        return null;
      } else {
        return user;
      }
    });
  }
}

I hope you can help me to understand the problem and solve it, thank you.

Upvotes: 2

Views: 1689

Answers (3)

Rafsan Uddin Beg Rizan
Rafsan Uddin Beg Rizan

Reputation: 2287

You can use my code, You can use userChanges() instead of authStateChanges()

final Stream<User?> firebaseUserChanges = firebaseAuth.userChanges();

Upvotes: 0

Frank van Puffelen
Frank van Puffelen

Reputation: 598740

Since authStateChanges returns a Stream, you'll want to use a StreamBuilder in your code to wrap that asynchronous operation too.

Something like this:

// once complete show your app
if (snapshot.connectionState == ConnectionState.done) {
  print('CONNECTED');
  return StreamBuilder(
    stream: FirebaseAuth.instance. authStateChanges(),
    builder: (BuildContext context, snapshot) {
      if (snapshot.hasData) {
        return MaterialApp(
          home: LoginPage(),
        );
      } else {
        return MaterialApp(
          home: HomePage(),
        );
      }
    }
  )
}

Unrelated: you're repeated the code to create a MaterialApp quite frequently, which is not needed. For example, in the above snippet we could have only one mention of MaterialApp and get the same result with:

// once complete show your app
if (snapshot.connectionState == ConnectionState.done) {
  print('CONNECTED');
  return StreamBuilder(
    stream: FirebaseAuth.instance. authStateChanges(),
    builder: (BuildContext context, snapshot) {
      return MaterialApp(
        home: snapshot.hasData && snapshot.data != null ? HomePage() : LoginPage(),
      )
    }
  )
}

If you do this for all mentions of MaterialApp and other duplication, you can reduce the code significantly, making it less error prone and easier to maintain.

Upvotes: 4

Doug Stevenson
Doug Stevenson

Reputation: 317392

It does persist. You are just not using the auth state listener correctly. The return statements from your authStateChanges listener are not actually escaping the call to user(). On top of that, the listener could return null the first time. It's not until some time later that the Firebase SDK determines that the user is actually valid and signed in. Your listener will get a second callback at that time. Your code need to be ready for this to happen - it can't just blindly take the first value, as the auth state might change over time.

I suggest adding some debug logging in your auth state listener to see how this actually works. Also I suggest reading this blog to understand how auth state listeners work in more detail.

Upvotes: 1

Related Questions