Login screen route comes before any other route when hotreload Flutter

1.5k views Asked by At

I use firebase to authenticate, and to know the user current state I use authStateChanges function inside a Stream function, and it works fine but when the user is logged-in, it first goes to the login screen for a fraction of second then it goes to the user/home page .. I don't understand why it goes to the login screen first!! It doesn't stay there, but it goes there first then it opens my home page !!

Here is my code ::

Main.dart

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: landingpage(),
    );
  }
}

landingPage.dart

class landingpage extends StatelessWidget {
  final Future<FirebaseApp> _initialization = Firebase.initializeApp();
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: _initialization,
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return Scaffold(
              body: Text('Error: ${snapshot.error}'),
            );
          }
          if (snapshot.connectionState == ConnectionState.done) {
            return MultiProvider(
              providers: [
                ChangeNotifierProvider<ModalHudState>(
                  create: (context) => ModalHudState(),
                ),
                ChangeNotifierProvider<AdminMode>(
                  create: (context) => AdminMode(),
                ),
                StreamProvider.value(value: Auth().user),
              ],
              child: MaterialApp(
                initialRoute: Wrapper.id,
                routes: routes,
              ),
            );
          }
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        });
  }
}

wrapper.dart

class _WrapperState extends State<Wrapper> {
  @override
  void initState() {
    super.initState();
    Loading();
  }

  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);

    //return either HomePage or Login_Screen
    if (user != null) {
      return HomePage();
    } else {
      return LoginScreen();
    }
  }
}

Auth.dart

class Auth {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  //Sign up method
  Future SignupClass(String email, String password) async {
    final UserCredential user = await _auth.createUserWithEmailAndPassword(
        email: email, password: password);
    return user.user;
  }

  //auth change stream
  Stream<User> get user {
    return _auth.authStateChanges();
  }

  //Sign in method
  Future<User> SigninClass(String email, String password) async {
    try {
      final user = await _auth.signInWithEmailAndPassword(
          email: email, password: password);
      return user.user;
    } catch (e) {
      print(e.toString());
      return null;
    }
  }

  //Sign out
  Future SignOut() async {
    try {
      return await _auth.signOut();
    } catch (e) {
      print(e.toString());
      return null;
    }
  }
}

routes.dart

var routes = {
  Wrapper.id: (context) => Wrapper(),
  Loading.id: (context) => Loading(),
  LoginScreen.id: (context) => LoginScreen(),
  SignupScreen.id: (context) => SignupScreen(),
  HomePage.id: (context) => HomePage(),
  AdminPage.id: (context) => AdminPage(),
  AddProduct.id: (context) => AddProduct(),
  EditProduct.id: (context) => EditProduct(),
};
2

There are 2 answers

2
Doug Stevenson On BEST ANSWER

authStateChanges does not emit data immediately upon launch of the app. It can take some amount of time for the Firebase Auth SDK to figure out of the current user is valid. Sometimes this involves making a network request, which can take a long time.

If you want to avoid the problem you're observing, your code should show some sort of splash screen or loading indicator until the auth state indicates that there is definitely a user signed in or not.

0
Siddharth Agrawal On

As Doug Stevenson changed, authStateChanges does not return a null object while it is fetching. It gives a future object. You can use an await function with a loading widget. Follow this answer to make it
https://stackoverflow.com/questions/64158249/show-progress-indicator-when-waiting-for-initializing-app-for-example-when-authe/64159081#64159081\
In the body, make it a stack. Let the first child be your main widget, then the next be a center widget with its child being a "Loading widget" like this

Stack(
 children:[Container(),
  Center(
   child:isLoading?Loading():Container()   
)]
)

Define an isLoading boolean variable and declare it false. Whenever you have to pause to authenticate, write it like this

setState((){
isLoading = true
})
await {Your authentication stuff}
setState((){
isLoading = false
})

And the loading widget can be your splash screen.