Google Auth auto logs in - firebase

I have implemented Google sign in to my mobile application, however, it asked me once for my e-mail and password. When I run the app again it skips the login screen and automatically navigates to the home screen.
I tried:
Deleting the app on the menu.
Clearing the cache on settings. (I guess I couldn't do it properly not for sure)
Even deleted the profile which automatically logs in. (still holds this profile as user idk how...)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './components/google_sign_in.dart';
import 'components/body.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../home/home_screen.dart';
class SignInScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider(
create: (context) => GoogleSignInProvider(),
child: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
final provider = Provider.of<GoogleSignInProvider>(context);
if (provider.isSigningIn) {
return buildLodading();
} else if (snapshot.hasData) {
print("data: ${snapshot.data}");
return HomeScreen();
} else {
return Body();
}
})));
}
Widget buildLodading() => Center(child: CircularProgressIndicator());
}
Body() = The login screen.
The program always returns true on snapshot.hasData so that it doesn't go in else.

What you're describing is the expected behavior. When you restart the app, Firebase implicitly tries to restore the user credentials, so that the user doesn't have to sign in each time they start the app.
If you want the user to explicitly require the user to provide their sign-in credentials each time they start the app, sign any existing user out when the app starts.
For example:
void main() {
WidgetsFlutterBinding.ensureInitialized();
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
FirebaseAuth.instance.signOut();
runApp(App());
}

Related

Auth Change listener in Firebase Auth not listening after pushing Splash screen in Flutter

The Problem
In my main.dart file, I using the Firebase AuthChange listener to listen for sign in and sign out. But the problem is that when my app is first launched it checks the auth state and returns a splash screen. If the user is logged out, I have a splash screen that pushes the login screen and if the user is logged in, I have a different splash screen that pushes the home screen.
Once any screen is pushed, it doesn't listen to any auth changes because the splash screen has pushed another screen. Is there any way to overcome this or can we integrate the splash screen into the main.dart file?
My Code
This is the main.dart file
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MaterialApp(
home: Main()
)
);
}
class Main extends StatelessWidget {
Main({Key? key}) : super(key: key);
FirebaseAuth auth = FirebaseAuth.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return const Splash2();
} else {
return const Splash();
}
},
),
);
}
}
Thanks!

Want to show Welcome Page to new user and Home Screen to Logged in users in flutter Firebase

I have this Flutter App which shows some data to logged-in users but every time even a logged-in user open the app will open to welcome screen I want the welcome screen to open only when a user logs out or haven't signed in, to begin with.
This is the main.dart:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:masterpass/Screens/Welcome/welcome_screen.dart';
import 'package:masterpass/components/constants.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MasterPass',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: kPrimaryColor,
scaffoldBackgroundColor: Colors.white,
),
home: WelcomeScreen(),
);
}
}
code on github: MasterPass
You could do that with a StreamBuilder:
StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (_, snapshot) {
if (snapshot.data == null) { //if user isn't signed in
return LoginScreen();
}
return HomeSite(); //if user is signed in
});
You can check if user is logged using FirebaseAuth.instance.currentUser != null and use that logic to set the home page.
for example:
home: (FirebaseAuth.instance.currentUser != null)
? HomePageWhenLoggedIn()
: WelcomeScreen(),

Flutter Firebase Authentication : authStateChanges() method in StreamBuilder does not work in order to change from LoginScreen to Dashboard

I am using StreamBuilder inside the home property of MaterialApp() widget in the main.dart file in order to navigate between the WelcomeScreen (containing login and signup buttons) and the DashboardScreen with the help of the authStateChanges() provided by firebase_auth package. But whenever I am logging in or signing up it stays on that screen only, until I manually Hot Reload the app. I have to hot reload the app in order to get from loginscreen to dashboard. The firebase auth is working correctly. I am able to see the user signed up in the firebase console. But the screen is not changing automatically. Also, there is no Navigator.push() method after the login or signup code.
main.dart:
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:banquet_owner_app/screens/welcome_screen.dart';
import 'package:banquet_owner_app/screens/dashboard.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, userSnapshot) {
if (userSnapshot.hasData) {
return Dashboard();
}
return WelcomeScreen();
},
),
);
}
}
you need to get the state of the stream ,then check if it has data
here is how to go about it
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, userSnapshot) {
if (userSnapshot.connectionState == ConnectionState.waiting) {
//loading screen
}
if (userSnapshot.connectionState == ConnectionState.done) {
//state is loaded check if snapshot has data
if (userSnapshot.hasData) {
return Dashboard();
}else{
return WelcomeScreen();
}
},

Flutter : Why does the login page appear briefly before going to home screen every time I restart my app although the user is already logged in?

Flutter & Firebase : Why does the login page always appear briefly before going to home screen every time I restart my app even though the user is already logged in?
I understand that my app needs sometimes to render the user from firebase. But the user is already logged anyway. So how can I proceed the home screen immediately since the user is already logged in? Is there a way to save the user data into the phone memory?
class _WrapperState extends State<Wrapper> {
#override
Widget build(BuildContext context) {
final user = Provider.of<SystemUser>(context);
if (user == null) {
print('In Authenicate or Login');
return Authenticate();
} else {
print('In HomeScreen');
return NavigationWrapper(); // goto homescreen
}
}
}
Please have a look and give some pointers.
You can save user auth state in shared preferences, but even in that case you will need to load some data before navigate to auth screen or home screen.
I recommend you use the following solution. This way you load auth state and then navigate the corresponding pages according to the auth state.
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class _WrapperState extends State<Wrapper> {
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: Provider.of<SystemUser>(context).isAuthenticate(),
builder: (context, snapshot) {
// while loading data
if (snapshot.data == null) {
return CircularProgressIndicator();
}
// if has error
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
// retrieve data - check for authentication
// authenticated, go to homescreen
if (snapshot.data == true) {
print('In HomeScreen');
return NavigationWrapper();
}
// not authenticated, go to auth screen
print('In Authenicate or Login');
return Authenticate();
},
);
}
}
// in SystemUser provider
// check for auth state and return corresponding value
Future<bool> isAuthenticate() async {
// you can implement shared prefereces
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('authKey') ?? false;
}
// whenever you logged in the user just call
prefs.setString('authKey', true);
What I suggest You to do is to use StreamBuilder to check if the user is logged in or not like this:
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, AsyncSnapshot<User> snapshot) {
if(snapshot.connectionState == ConnectionState.waiting)
return Center(child: CupertinoActivityIndicator());
else if(!snapshot.hasData || snapshot.data == null)
return LoginPage();
else if (snapshot.hasError)
return Center(child: Text('${snapshot.error}'));
return HomePage();
},
);
}
}
I use it like this in every project that needs FirebaseAuth, I hope it helps.
You can do the following using the Consumer element.
Install the Provider package for flutter and make a bool isAuth. When the user logs in, set isAuth = true, then add an if statement inside home: like in the image I provided. Hopefully that will solve your problem.

Stateless Widget not Updating with StreamProvider

I am trying to use StreamProvider to get my app to display the Features widget when a user is signed in. Here is my code:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'auth/auth.dart';
import 'features/features.dart';
void main() => runApp(
StreamProvider<FirebaseUser>(
create: (context) => FirebaseAuth.instance.onAuthStateChanged,
child: MaterialApp(home: MyApp()),
updateShouldNotify: (_, __) => true),
);
class MyApp extends StatelessWidget {
static reload(BuildContext context) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => MyApp()),
(Route<dynamic> route) => false,
);
}
#override
Widget build(BuildContext context) {
var _user = Provider.of<FirebaseUser>(context);
print('USER ISSUED: ' + _user.toString());
if (_user == null) return SignInPage();
if (_user != null) return Features();
}
}
I had to create the reload() method, which is called by the various authentication widgets (google, facebook, password) nested below SignInPage to update display of MyApp. I would have expected that Provider.of<FirebaseUser> would rebuild my app automatically, without need to call reload(). The Provider does work. Whenever FirebaseAuth updates the user, it prints USER ISSUED: some_user.... Why does it not execute the next two lines of code to return either my SignInPage when user == null or the Features page when a user is logged in successfully?
I'm new to FlutterFire, but coming from AngularFire, I would have expected this to work smoothly.
I think you should be using:
// (firebase_auth 0.9.0)
StreamProvider.value(
value: FirebaseAuth.instance.onAuthStateChanged,
child: MaterialApp(home: MyApp()),
),
because as per the docs StreamProvider.value listens to value and expose it to all of StreamProvider descendants.
However, using StreamBuilder shall be preferred here as you might want to check if connection is established or not, else everytime the app opens it may show a glimpse of the login screen and then automatically go to the home screen in moderate network conditions, if user authentication is cached.
// (fireabse_auth 0.18.4)
StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (_, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Loading();
}
if (snapshot.data is User && snapshot.data != null) {
return Home();
}
return Authenticate();
})
Also,
I would have expected that Provider.of would rebuild my app automatically
It's StreamProvider that is responsible for listening to the stream, exposing it to the descendants and updating the consumer's state, and not Provider.of<T>(context). Provider.of<T>(context) just attempts to access the data of type T from the nearest parent provider of T.

Resources