WizardingStudios
WizardingStudios

Reputation: 572

Flutter StreamBuilder to monitor for Firebase Auth state

When I have a logged user, my wrapper check for additional data and once find it will open the HomeScreen of my application.

However, the Stream Builder does not return the SignIn screen even if user successfully logout.

As I can see the Home page is built in a different tree (but how to build it in the main tree?), don't know if this should interfere.

Main code:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  if (kIsWeb) {
    await Firebase.initializeApp(),
    );
  } else {
    await Firebase.initializeApp();
  }
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        Provider<AuthService>(
          create: (_) => AuthService(),
        ),
      ],
      child: GestureDetector(
        onTap: () {
          FocusScopeNode currentFocus = FocusScope.of(context);
          if (!currentFocus.hasPrimaryFocus) {
            FocusScope.of(context).requestFocus(FocusNode());
          }
        },
        child: MaterialApp(
          theme: ThemeData(
            primaryColor: appColor,
            primaryColorLight: appColor,
            primaryColorDark: appColor,
          ),
          debugShowCheckedModeBanner: false,
          title: 'WhyBye',
          //todo: Queste righe saranno necessarie quando avremo anche la versione WEB
          // home: ResponsiveLayout(
          //     webScreenLayout: WebScreenLayout(),
          //     mobileScreenLayout: MobileScreenLayout()),
          //todo.

          initialRoute: '/wrapper',
          routes: {
            '/wrapper': (context) => const Wrapper(),
            '/signInScreen': (context) => const SignInScreen(),
            '/signUpScreen': (context) => const SignUpScreen(),
            '/registrationScreen': (context) => const RegistrationScreen(),
            '/homeScreen': (context) => const HomeScreen(),
            '/recoverPasswordScreen': (context) =>
                const RecoverPasswordScreen(),
          },
        ),
      ),
    );
  }
}

My Wrapper code:

class Wrapper extends StatelessWidget {
  const Wrapper({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final authService = Provider.of<AuthService>(context);
    return StreamBuilder<User?>(
      stream: authService.user,
      builder: (_, AsyncSnapshot<User?> snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          final User? user = snapshot.data;
          //return user == null ? const SignInScreen() : const HomeScreen();
          if (user == null) {
            return const SignInScreen();
          } else {
            WidgetsBinding.instance
                ?.addPostFrameCallback((_) => asyncMethod(context));
            return const Scaffold(
              body: Center(
                child: CircularProgressIndicator(
                  color: appBarColor,
                ),
              ),
            );
          }
        } else {
          return const Scaffold(
            body: Center(
              child: CircularProgressIndicator(
                color: appBarColor,
              ),
            ),
          );
        }
      },
    );
  }
}

void asyncMethod(BuildContext context) async {
  final FirebaseMethods _firebase = FirebaseMethods();
  bool isTrue = await _firebase.isRegistrationUserCompleted(_firebase.getUID());
  if (!isTrue) {
    Variables.isCustomUserPic = false;
    Variables.isSocialUserPic = false;
    await _firebase.loadDefaultUserPicUrl();
    //& qui devo andare alla registrazione utente.
    if (Variables.isSignUp) {
      Navigator.of(context).pushReplacement(
        MaterialPageRoute(builder: (context) => const RegistrationScreen()),
      );
    } else {
      Navigator.of(context).push(
        MaterialPageRoute(builder: (context) => const RegistrationScreen()),
      );
    }
    //Navigator.pop(context);
  } else {
    Navigator.of(context).push(
      MaterialPageRoute(builder: (context) => const HomeScreen()),
    );
    //Navigator.pop(context);
  }
}

And my very simple Home Screen:

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return const IHome();
  }
}

class IHome extends StatelessWidget {
  const IHome({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Center(
        child: ElevatedButton(
          child: const Text('LogOut'),
          onPressed: () async {
            bool isTrue = await AuthMethods().signOut();
            if (isTrue) {
              //Navigator.pop(context);
            }
            //Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

here the widget tree when a user is logged in and the wrapper loads the Home Screen.

enter image description here

As you can see I need to check other data before to switch to Home Screen, it's a post-account data that I need.

However when a user is logged in I open the Home Page directly from the Wrapper, but the Logout is not recognised (I checked the logout works well).

Here the widget tree when the wrapper chose the SignIn screen (because the user is not logged in).

enter image description here

As I should understand I probably need to avoid to use Navigator to open HomeScreen, but I don't know how to do.

Many thanks for support.

Upvotes: 3

Views: 1326

Answers (1)

Tim
Tim

Reputation: 171

There could be a couple of reasons why you don't get rerouted when the authStatechange:

could be a major issue with how your provider and state notifiers are created and are being used.

I suggest you listen for authStateChange in your Wrapper Widget instead, here is a brief example:

@override
Widget build(BuildContext context) {
  return MaterialApp(
      title: 'Your App Name',
      home: _getLandingPage()
  );
}

Widget _getLandingPage() {
  return StreamBuilder<FirebaseUser>(
    stream: FirebaseAuth.instance.onAuthStateChanged,
    builder: (BuildContext context, snapshot) {
      if (snapshot.hasData) {
         return MainPage();
      } else {
        return LoginPage();
      }
    },
  );
}

OR

FirebaseAuth.instance
  .userChanges()
  .listen((User? user) {
    if (user == null) {
      print('User is currently signed out!');
    } else {
      print('User is signed in!');
    }
  });

You can find more info here

Upvotes: 1

Related Questions